diff --git a/Lib/site-packages/adodbapi/__init__.py b/Lib/site-packages/adodbapi/__init__.py
new file mode 100644
index 0000000..a5eca81
--- /dev/null
+++ b/Lib/site-packages/adodbapi/__init__.py
@@ -0,0 +1,56 @@
+"""adodbapi - A python DB API 2.0 (PEP 249) interface to Microsoft ADO
+
+Copyright (C) 2002 Henrik Ekelund, version 2.1 by Vernon Cole
+* http://sourceforge.net/projects/adodbapi
+"""
+import sys
+import time
+
+if sys.version_info < (3,0): # in Python 2, define all symbols, just like the bad old way
+ from apibase import *
+ VariantConversionMap = MultiMap # old name. Should use apibase.MultiMap
+ from .ado_consts import *
+ _makeByteBuffer = buffer
+else:
+ # but if the user is running Python 3, then keep the dictionary clean
+ from .apibase import apilevel, threadsafety, paramstyle
+ from .apibase import Warning, Error, InterfaceError, DatabaseError, DataError, OperationalError, IntegrityError
+ from .apibase import InternalError, ProgrammingError, NotSupportedError, FetchFailedError
+ from .apibase import NUMBER, STRING, BINARY, DATETIME, ROWID
+ _makeByteBuffer = bytes
+
+from .adodbapi import connect, Connection, __version__, dateconverter, Cursor
+
+def Binary(aString):
+ """This function constructs an object capable of holding a binary (long) string value. """
+ return _makeByteBuffer(aString)
+
+def Date(year,month,day):
+ "This function constructs an object holding a date value. "
+ return dateconverter.Date(year,month,day)
+
+def Time(hour,minute,second):
+ "This function constructs an object holding a time value. "
+ return dateconverter.Time(hour,minute,second)
+
+def Timestamp(year,month,day,hour,minute,second):
+ "This function constructs an object holding a time stamp value. "
+ return dateconverter.Timestamp(year,month,day,hour,minute,second)
+
+def DateFromTicks(ticks):
+ """This function constructs an object holding a date value from the given ticks value
+ (number of seconds since the epoch; see the documentation of the standard Python time module for details). """
+ return Date(*time.gmtime(ticks)[:3])
+
+def TimeFromTicks(ticks):
+ """This function constructs an object holding a time value from the given ticks value
+ (number of seconds since the epoch; see the documentation of the standard Python time module for details). """
+ return Time(*time.gmtime(ticks)[3:6])
+
+def TimestampFromTicks(ticks):
+ """This function constructs an object holding a time stamp value from the given
+ ticks value (number of seconds since the epoch;
+ see the documentation of the standard Python time module for details). """
+ return Timestamp(*time.gmtime(ticks)[:6])
+
+version = 'adodbapi v' + __version__
diff --git a/Lib/site-packages/adodbapi/ado_consts.py b/Lib/site-packages/adodbapi/ado_consts.py
new file mode 100644
index 0000000..5ecf735
--- /dev/null
+++ b/Lib/site-packages/adodbapi/ado_consts.py
@@ -0,0 +1,276 @@
+# ADO enumerated constants documented on MSDN:
+# http://msdn.microsoft.com/en-us/library/ms678353(VS.85).aspx
+
+# IsolationLevelEnum
+adXactUnspecified = -1
+adXactBrowse = 0x100
+adXactChaos = 0x10
+adXactCursorStability = 0x1000
+adXactIsolated = 0x100000
+adXactReadCommitted = 0x1000
+adXactReadUncommitted = 0x100
+adXactRepeatableRead = 0x10000
+adXactSerializable = 0x100000
+
+# CursorLocationEnum
+adUseClient = 3
+adUseServer = 2
+
+# CursorTypeEnum
+adOpenDynamic = 2
+adOpenForwardOnly = 0
+adOpenKeyset = 1
+adOpenStatic = 3
+adOpenUnspecified = -1
+
+# CommandTypeEnum
+adCmdText = 1
+adCmdStoredProc = 4
+adSchemaTables = 20
+
+# ParameterDirectionEnum
+adParamInput = 1
+adParamInputOutput = 3
+adParamOutput = 2
+adParamReturnValue = 4
+adParamUnknown = 0
+directions = {
+ 0: 'Unknown',
+ 1: 'Input',
+ 2: 'Output',
+ 3: 'InputOutput',
+ 4: 'Return',
+ }
+def ado_direction_name(ado_dir):
+ try:
+ return 'adParam' + directions[ado_dir]
+ except:
+ return 'unknown direction ('+str(ado_dir)+')'
+
+# ObjectStateEnum
+adStateClosed = 0
+adStateOpen = 1
+adStateConnecting = 2
+adStateExecuting = 4
+adStateFetching = 8
+
+# FieldAttributeEnum
+adFldMayBeNull = 0x40
+
+# ConnectModeEnum
+adModeUnknown = 0
+adModeRead = 1
+adModeWrite = 2
+adModeReadWrite = 3
+adModeShareDenyRead = 4
+adModeShareDenyWrite = 8
+adModeShareExclusive = 12
+adModeShareDenyNone = 16
+adModeRecursive = 0x400000
+
+# XactAttributeEnum
+adXactCommitRetaining = 131072
+adXactAbortRetaining = 262144
+
+ado_error_TIMEOUT = -2147217871
+
+# DataTypeEnum - ADO Data types documented at:
+# http://msdn2.microsoft.com/en-us/library/ms675318.aspx
+adArray = 0x2000
+adEmpty = 0x0
+adBSTR = 0x8
+adBigInt = 0x14
+adBinary = 0x80
+adBoolean = 0xb
+adChapter = 0x88
+adChar = 0x81
+adCurrency = 0x6
+adDBDate = 0x85
+adDBTime = 0x86
+adDBTimeStamp = 0x87
+adDate = 0x7
+adDecimal = 0xe
+adDouble = 0x5
+adError = 0xa
+adFileTime = 0x40
+adGUID = 0x48
+adIDispatch = 0x9
+adIUnknown = 0xd
+adInteger = 0x3
+adLongVarBinary = 0xcd
+adLongVarChar = 0xc9
+adLongVarWChar = 0xcb
+adNumeric = 0x83
+adPropVariant = 0x8a
+adSingle = 0x4
+adSmallInt = 0x2
+adTinyInt = 0x10
+adUnsignedBigInt = 0x15
+adUnsignedInt = 0x13
+adUnsignedSmallInt = 0x12
+adUnsignedTinyInt = 0x11
+adUserDefined = 0x84
+adVarBinary = 0xCC
+adVarChar = 0xC8
+adVarNumeric = 0x8B
+adVarWChar = 0xCA
+adVariant = 0xC
+adWChar = 0x82
+# Additional constants used by introspection but not ADO itself
+AUTO_FIELD_MARKER = -1000
+
+adTypeNames = {
+ adBSTR: 'adBSTR',
+ adBigInt: 'adBigInt',
+ adBinary: 'adBinary',
+ adBoolean: 'adBoolean',
+ adChapter: 'adChapter',
+ adChar: 'adChar',
+ adCurrency: 'adCurrency',
+ adDBDate: 'adDBDate',
+ adDBTime: 'adDBTime',
+ adDBTimeStamp: 'adDBTimeStamp',
+ adDate: 'adDate',
+ adDecimal: 'adDecimal',
+ adDouble: 'adDouble',
+ adEmpty: 'adEmpty',
+ adError: 'adError',
+ adFileTime: 'adFileTime',
+ adGUID: 'adGUID',
+ adIDispatch: 'adIDispatch',
+ adIUnknown: 'adIUnknown',
+ adInteger: 'adInteger',
+ adLongVarBinary: 'adLongVarBinary',
+ adLongVarChar: 'adLongVarChar',
+ adLongVarWChar: 'adLongVarWChar',
+ adNumeric: 'adNumeric',
+ adPropVariant: 'adPropVariant',
+ adSingle: 'adSingle',
+ adSmallInt: 'adSmallInt',
+ adTinyInt: 'adTinyInt',
+ adUnsignedBigInt: 'adUnsignedBigInt',
+ adUnsignedInt: 'adUnsignedInt',
+ adUnsignedSmallInt: 'adUnsignedSmallInt',
+ adUnsignedTinyInt: 'adUnsignedTinyInt',
+ adUserDefined: 'adUserDefined',
+ adVarBinary: 'adVarBinary',
+ adVarChar: 'adVarChar',
+ adVarNumeric: 'adVarNumeric',
+ adVarWChar: 'adVarWChar',
+ adVariant: 'adVariant',
+ adWChar: 'adWChar',
+ }
+
+def ado_type_name(ado_type):
+ return adTypeNames.get(ado_type, 'unknown type ('+str(ado_type)+')')
+
+# here in decimal, sorted by value
+#adEmpty 0 Specifies no value (DBTYPE_EMPTY).
+#adSmallInt 2 Indicates a two-byte signed integer (DBTYPE_I2).
+#adInteger 3 Indicates a four-byte signed integer (DBTYPE_I4).
+#adSingle 4 Indicates a single-precision floating-point value (DBTYPE_R4).
+#adDouble 5 Indicates a double-precision floating-point value (DBTYPE_R8).
+#adCurrency 6 Indicates a currency value (DBTYPE_CY). Currency is a fixed-point number
+# with four digits to the right of the decimal point. It is stored in an eight-byte signed integer scaled by 10,000.
+#adDate 7 Indicates a date value (DBTYPE_DATE). A date is stored as a double, the whole part of which is
+# the number of days since December 30, 1899, and the fractional part of which is the fraction of a day.
+#adBSTR 8 Indicates a null-terminated character string (Unicode) (DBTYPE_BSTR).
+#adIDispatch 9 Indicates a pointer to an IDispatch interface on a COM object (DBTYPE_IDISPATCH).
+#adError 10 Indicates a 32-bit error code (DBTYPE_ERROR).
+#adBoolean 11 Indicates a boolean value (DBTYPE_BOOL).
+#adVariant 12 Indicates an Automation Variant (DBTYPE_VARIANT).
+#adIUnknown 13 Indicates a pointer to an IUnknown interface on a COM object (DBTYPE_IUNKNOWN).
+#adDecimal 14 Indicates an exact numeric value with a fixed precision and scale (DBTYPE_DECIMAL).
+#adTinyInt 16 Indicates a one-byte signed integer (DBTYPE_I1).
+#adUnsignedTinyInt 17 Indicates a one-byte unsigned integer (DBTYPE_UI1).
+#adUnsignedSmallInt 18 Indicates a two-byte unsigned integer (DBTYPE_UI2).
+#adUnsignedInt 19 Indicates a four-byte unsigned integer (DBTYPE_UI4).
+#adBigInt 20 Indicates an eight-byte signed integer (DBTYPE_I8).
+#adUnsignedBigInt 21 Indicates an eight-byte unsigned integer (DBTYPE_UI8).
+#adFileTime 64 Indicates a 64-bit value representing the number of 100-nanosecond intervals since
+# January 1, 1601 (DBTYPE_FILETIME).
+#adGUID 72 Indicates a globally unique identifier (GUID) (DBTYPE_GUID).
+#adBinary 128 Indicates a binary value (DBTYPE_BYTES).
+#adChar 129 Indicates a string value (DBTYPE_STR).
+#adWChar 130 Indicates a null-terminated Unicode character string (DBTYPE_WSTR).
+#adNumeric 131 Indicates an exact numeric value with a fixed precision and scale (DBTYPE_NUMERIC).
+# adUserDefined 132 Indicates a user-defined variable (DBTYPE_UDT).
+#adUserDefined 132 Indicates a user-defined variable (DBTYPE_UDT).
+#adDBDate 133 Indicates a date value (yyyymmdd) (DBTYPE_DBDATE).
+#adDBTime 134 Indicates a time value (hhmmss) (DBTYPE_DBTIME).
+#adDBTimeStamp 135 Indicates a date/time stamp (yyyymmddhhmmss plus a fraction in billionths) (DBTYPE_DBTIMESTAMP).
+#adChapter 136 Indicates a four-byte chapter value that identifies rows in a child rowset (DBTYPE_HCHAPTER).
+#adPropVariant 138 Indicates an Automation PROPVARIANT (DBTYPE_PROP_VARIANT).
+#adVarNumeric 139 Indicates a numeric value (Parameter object only).
+#adVarChar 200 Indicates a string value (Parameter object only).
+#adLongVarChar 201 Indicates a long string value (Parameter object only).
+#adVarWChar 202 Indicates a null-terminated Unicode character string (Parameter object only).
+#adLongVarWChar 203 Indicates a long null-terminated Unicode string value (Parameter object only).
+#adVarBinary 204 Indicates a binary value (Parameter object only).
+#adLongVarBinary 205 Indicates a long binary value (Parameter object only).
+#adArray (Does not apply to ADOX.) 0x2000 A flag value, always combined with another data type constant,
+# that indicates an array of that other data type.
+
+# Error codes to names
+adoErrors= {
+ 0xe7b :'adErrBoundToCommand',
+ 0xe94 :'adErrCannotComplete',
+ 0xea4 :'adErrCantChangeConnection',
+ 0xc94 :'adErrCantChangeProvider',
+ 0xe8c :'adErrCantConvertvalue',
+ 0xe8d :'adErrCantCreate',
+ 0xea3 :'adErrCatalogNotSet',
+ 0xe8e :'adErrColumnNotOnThisRow',
+ 0xd5d :'adErrDataConversion',
+ 0xe89 :'adErrDataOverflow',
+ 0xe9a :'adErrDelResOutOfScope',
+ 0xea6 :'adErrDenyNotSupported',
+ 0xea7 :'adErrDenyTypeNotSupported',
+ 0xcb3 :'adErrFeatureNotAvailable',
+ 0xea5 :'adErrFieldsUpdateFailed',
+ 0xc93 :'adErrIllegalOperation',
+ 0xcae :'adErrInTransaction',
+ 0xe87 :'adErrIntegrityViolation',
+ 0xbb9 :'adErrInvalidArgument',
+ 0xe7d :'adErrInvalidConnection',
+ 0xe7c :'adErrInvalidParamInfo',
+ 0xe82 :'adErrInvalidTransaction',
+ 0xe91 :'adErrInvalidURL',
+ 0xcc1 :'adErrItemNotFound',
+ 0xbcd :'adErrNoCurrentRecord',
+ 0xe83 :'adErrNotExecuting',
+ 0xe7e :'adErrNotReentrant',
+ 0xe78 :'adErrObjectClosed',
+ 0xd27 :'adErrObjectInCollection',
+ 0xd5c :'adErrObjectNotSet',
+ 0xe79 :'adErrObjectOpen',
+ 0xbba :'adErrOpeningFile',
+ 0xe80 :'adErrOperationCancelled',
+ 0xe96 :'adErrOutOfSpace',
+ 0xe88 :'adErrPermissionDenied',
+ 0xe9e :'adErrPropConflicting',
+ 0xe9b :'adErrPropInvalidColumn',
+ 0xe9c :'adErrPropInvalidOption',
+ 0xe9d :'adErrPropInvalidValue',
+ 0xe9f :'adErrPropNotAllSettable',
+ 0xea0 :'adErrPropNotSet',
+ 0xea1 :'adErrPropNotSettable',
+ 0xea2 :'adErrPropNotSupported',
+ 0xbb8 :'adErrProviderFailed',
+ 0xe7a :'adErrProviderNotFound',
+ 0xbbb :'adErrReadFile',
+ 0xe93 :'adErrResourceExists',
+ 0xe92 :'adErrResourceLocked',
+ 0xe97 :'adErrResourceOutOfScope',
+ 0xe8a :'adErrSchemaViolation',
+ 0xe8b :'adErrSignMismatch',
+ 0xe81 :'adErrStillConnecting',
+ 0xe7f :'adErrStillExecuting',
+ 0xe90 :'adErrTreePermissionDenied',
+ 0xe8f :'adErrURLDoesNotExist',
+ 0xe99 :'adErrURLNamedRowDoesNotExist',
+ 0xe98 :'adErrUnavailable',
+ 0xe84 :'adErrUnsafeOperation',
+ 0xe95 :'adErrVolumeNotFound',
+ 0xbbc :'adErrWriteFile'
+ }
diff --git a/Lib/site-packages/adodbapi/adodbapi.py b/Lib/site-packages/adodbapi/adodbapi.py
new file mode 100644
index 0000000..0100df7
--- /dev/null
+++ b/Lib/site-packages/adodbapi/adodbapi.py
@@ -0,0 +1,1010 @@
+"""adodbapi - A python DB API 2.0 (PEP 249) interface to Microsoft ADO
+
+Copyright (C) 2002 Henrik Ekelund, versions 2.1 and later by Vernon Cole
+* http://sourceforge.net/projects/pywin32
+* http://sourceforge.net/projects/adodbapi
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ django adaptations and refactoring by Adam Vandenberg
+
+DB-API 2.0 specification: http://www.python.org/dev/peps/pep-0249/
+
+This module source should run correctly in CPython versions 2.5 and later,
+or IronPython version 2.6 and later,
+or, after running through 2to3.py, CPython 3.0 or later.
+"""
+__version__ = '2.6.0.6'
+version = 'adodbapi v' + __version__
+
+import sys
+import copy
+import decimal
+import os
+
+import process_connect_string
+from . import ado_consts as adc
+import apibase as api
+
+try:
+ verbose = int(os.environ['ADODBAPI_VERBOSE'])
+except:
+ verbose = False
+if verbose:
+ print(version)
+
+# --- define objects to smooth out IronPython <-> CPython differences
+onWin32 = False # assume the worst
+if api.onIronPython:
+ from System import Activator, Type, DBNull, DateTime, Array, Byte
+ from System import Decimal as SystemDecimal
+ from clr import Reference
+ def Dispatch(dispatch):
+ type = Type.GetTypeFromProgID(dispatch)
+ return Activator.CreateInstance(type)
+ def getIndexedValue(obj,index):
+ return obj.Item[index]
+else: # try pywin32
+ try:
+ import win32com.client
+ import pythoncom
+ import pywintypes
+ onWin32 = True
+ def Dispatch(dispatch):
+ return win32com.client.Dispatch(dispatch)
+ except ImportError:
+ import warnings
+ warnings.warn("pywin32 package (or IronPython) required for adodbapi.",ImportWarning)
+ def getIndexedValue(obj,index):
+ return obj(index)
+
+try:
+ from collections import Mapping
+except ImportError: # Python 2.5
+ Mapping = dict # this will handle the most common case
+
+# --- define objects to smooth out Python3000 <-> Python 2.x differences
+unicodeType = str #this line will be altered by 2to3.py to '= str'
+longType = int #this line will be altered by 2to3.py to '= int'
+if sys.version_info >= (3,0): #python 3.x
+ StringTypes = str
+ maxint = sys.maxsize
+else: #python 2.x
+ StringTypes = (str,str) # will be messed up by 2to3 but never used
+ maxint = sys.maxint
+
+# ----------------- The .connect method -----------------
+def make_COM_connecter():
+ try:
+ if onWin32:
+ pythoncom.CoInitialize() #v2.1 Paj
+ c = Dispatch('ADODB.Connection') #connect _after_ CoIninialize v2.1.1 adamvan
+ except:
+ raise api.InterfaceError ("Windows COM Error: Dispatch('ADODB.Connection') failed.")
+ return c
+
+def connect(*args, **kwargs): # --> a db-api connection object
+ """Connect to a database.
+
+ call using:
+ :connection_string -- An ADODB formatted connection string, see:
+ * http://www.connectionstrings.com
+ * http://www.asp101.com/articles/john/connstring/default.asp
+ :timeout -- A command timeout value, in seconds (default 30 seconds)
+ """
+ co = Connection() # make an empty connection object
+
+ kwargs = process_connect_string.process(args, kwargs, True)
+
+ try: # connect to the database, using the connection information in kwargs
+ co.connect(kwargs)
+ return co
+ except (Exception) as e:
+ message = 'Error opening connection to "%s"' % co.connection_string
+ raise api.OperationalError(e, message)
+
+# so you could use something like:
+# myConnection.paramstyle = 'named'
+# The programmer may also change the default.
+# For example, if I were using django, I would say:
+# import adodbapi as Database
+# Database.adodbapi.paramstyle = 'format'
+
+# ------- other module level defaults --------
+defaultIsolationLevel = adc.adXactReadCommitted
+# Set defaultIsolationLevel on module level before creating the connection.
+# For example:
+# import adodbapi, ado_consts
+# adodbapi.adodbapi.defaultIsolationLevel=ado_consts.adXactBrowse"
+#
+# Set defaultCursorLocation on module level before creating the connection.
+# It may be one of the "adUse..." consts.
+defaultCursorLocation = adc.adUseClient # changed from adUseServer as of v 2.3.0
+
+dateconverter = api.pythonDateTimeConverter() # default
+
+def format_parameters(ADOparameters, show_value=False):
+ """Format a collection of ADO Command Parameters.
+
+ Used by error reporting in _execute_command.
+ """
+ try:
+ if show_value:
+ desc = [
+ "Name: %s, Dir.: %s, Type: %s, Size: %s, Value: \"%s\", Precision: %s, NumericScale: %s" %\
+ (p.Name, adc.directions[p.Direction], adc.adTypeNames.get(p.Type, str(p.Type)+' (unknown type)'), p.Size, p.Value, p.Precision, p.NumericScale)
+ for p in ADOparameters ]
+ else:
+ desc = [
+ "Name: %s, Dir.: %s, Type: %s, Size: %s, Precision: %s, NumericScale: %s" %\
+ (p.Name, adc.directions[p.Direction], adc.adTypeNames.get(p.Type, str(p.Type)+' (unknown type)'), p.Size, p.Precision, p.NumericScale)
+ for p in ADOparameters ]
+ return '[' + '\n'.join(desc) + ']'
+ except:
+ return '[]'
+
+def _configure_parameter(p, value, adotype, settings_known):
+ """Configure the given ADO Parameter 'p' with the Python 'value'."""
+
+ if adotype in api.adoBinaryTypes:
+ p.Size = len(value)
+ p.AppendChunk(value)
+
+ elif isinstance(value,StringTypes): #v2.1 Jevon
+ L = len(value)
+ if adotype in api.adoStringTypes: #v2.2.1 Cole
+ if settings_known: L = min(L,p.Size) #v2.1 Cole limit data to defined size
+ p.Value = value[:L] #v2.1 Jevon & v2.1 Cole
+ else:
+ p.Value = value # dont limit if db column is numeric
+ if L>0: #v2.1 Cole something does not like p.Size as Zero
+ p.Size = L #v2.1 Jevon
+
+ elif isinstance(value, decimal.Decimal):
+ if api.onIronPython:
+ s = str(value)
+ p.Value = s
+ p.Size = len(s)
+ else:
+ p.Value = value
+ exponent = value.as_tuple()[2]
+ digit_count = len(value.as_tuple()[1])
+ p.Precision = digit_count
+ if exponent == 0:
+ p.NumericScale = 0
+ elif exponent < 0:
+ p.NumericScale = -exponent
+ if p.Precision < p.NumericScale:
+ p.Precision = p.NumericScale
+ else: # exponent > 0:
+ p.NumericScale = 0
+ p.Precision = digit_count + exponent
+
+ elif type(value) in dateconverter.types:
+ if settings_known and adotype in api.adoDateTimeTypes:
+ p.Value = dateconverter.COMDate(value)
+ else: #probably a string
+ # provide the date as a string in the format 'YYYY-MM-dd'
+ s = dateconverter.DateObjectToIsoFormatString(value)
+ p.Value = s
+ p.Size = len(s)
+
+ elif api.onIronPython and isinstance(value, longType): # Iron Python Long
+ s = str(value) # feature workaround for IPy 2.0
+ p.Value = s
+
+ elif adotype == adc.adEmpty: # ADO will not let you specify a null column
+ p.Type = adc.adInteger # so we will fake it to be an integer (just to have something)
+ p.Value = None # and pass in a Null *value*
+
+ # For any other type, set the value and let pythoncom do the right thing.
+ else:
+ p.Value = value
+
+
+def counter():
+ i = 0
+ while True:
+ yield i
+ i += 1
+
+# # # # # ----- the Class that defines a connection ----- # # # # #
+class Connection(object):
+ # include connection attributes as class attributes required by api definition.
+ Warning = api.Warning
+ Error = api.Error
+ InterfaceError = api.InterfaceError
+ DataError = api.DataError
+ DatabaseError = api.DatabaseError
+ OperationalError = api.OperationalError
+ IntegrityError = api.IntegrityError
+ InternalError = api.InternalError
+ NotSupportedError = api.NotSupportedError
+ ProgrammingError = api.ProgrammingError
+ FetchFailedError = api.FetchFailedError # (special for django)
+ # ...class attributes... (can be overridden by instance attributes)
+ verbose = api.verbose
+
+ @property
+ def dbapi(self): # a proposed db-api version 3 extension.
+ "Return a reference to the DBAPI module for this Connection."
+ return api
+
+ def __init__(self): # now define the instance attributes
+ self.connector = None
+ self.paramstyle = api.paramstyle
+ self.supportsTransactions = False
+ self.connection_string = ''
+ self.cursors = {}
+ self.cursor_counter = counter()
+ self.dbms_name = ''
+ self.dbms_version = ''
+ self.errorhandler = None # use the standard error handler for this instance
+ self.transaction_level = 0 # 0 == Not in a transaction, at the top level
+ self._autocommit = False
+
+ def connect(self, kwargs, connection_maker=make_COM_connecter):
+ if verbose > 9:
+ print(('kwargs=', repr(kwargs)))
+ try:
+ self.connection_string = kwargs['connection_string'] % kwargs # insert keyword arguments
+ except (Exception) as e:
+ self._raiseConnectionError(KeyError,'Python string format error in connection string->')
+ self.timeout = kwargs.get('timeout', 30)
+ self.kwargs = kwargs
+ if verbose:
+ print('%s attempting: "%s"' % (version, self.connection_string))
+ self.connector = connection_maker()
+ self.connector.ConnectionTimeout = self.timeout
+ self.connector.ConnectionString = self.connection_string
+
+ try:
+ self.connector.Open() # Open the ADO connection
+ except api.Error:
+ self._raiseConnectionError(api.DatabaseError, 'ADO error trying to Open=%s' % self.connection_string)
+
+ if getIndexedValue(self.connector.Properties,'Transaction DDL').Value != 0:
+ self.supportsTransactions=True
+ self.dbms_name = getIndexedValue(self.connector.Properties,'DBMS Name').Value
+ self.dbms_version = getIndexedValue(self.connector.Properties,'DBMS Version').Value
+ self.connector.CursorLocation = defaultCursorLocation #v2.1 Rose
+ if self.supportsTransactions:
+ self.connector.IsolationLevel=defaultIsolationLevel
+ self._autocommit = bool(kwargs.get('autocommit', False))
+ if not self._autocommit:
+ self.transaction_level = self.connector.BeginTrans() #Disables autocommit & inits transaction_level
+ else:
+ self._autocommit = True
+ if 'paramstyle' in kwargs:
+ self.paramstyle = kwargs['paramstyle'] # let setattr do the error checking
+ self.messages=[]
+ if verbose:
+ print('adodbapi New connection at %X' % id(self))
+
+ def _raiseConnectionError(self, errorclass, errorvalue):
+ eh = self.errorhandler
+ if eh is None:
+ eh = api.standardErrorHandler
+ eh(self, None, errorclass, errorvalue)
+
+ def _closeAdoConnection(self): #all v2.1 Rose
+ """close the underlying ADO Connection object,
+ rolling it back first if it supports transactions."""
+ if self.connector is None:
+ return
+ if not self._autocommit:
+ if self.transaction_level:
+ try: self.connector.RollbackTrans()
+ except: pass
+ self.connector.Close()
+ if verbose:
+ print('adodbapi Closed connection at %X' % id(self))
+
+ def close(self):
+ """Close the connection now (rather than whenever __del__ is called).
+
+ The connection will be unusable from this point forward;
+ an Error (or subclass) exception will be raised if any operation is attempted with the connection.
+ The same applies to all cursor objects trying to use the connection.
+ """
+ for crsr in list(self.cursors.values())[:]: # copy the list, then close each one
+ crsr.close(dont_tell_me=True)
+ self.messages=[]
+ try:
+ self._closeAdoConnection() #v2.1 Rose
+ except (Exception) as e:
+ self._raiseConnectionError(sys.exc_info()[0], sys.exc_info()[1])
+
+ self.connector = None #v2.4.2.2 fix subtle timeout bug
+ # per M.Hammond: "I expect the benefits of uninitializing are probably fairly small,
+ # so never uninitializing will probably not cause any problems."
+
+ def commit(self):
+ """Commit any pending transaction to the database.
+
+ Note that if the database supports an auto-commit feature,
+ this must be initially off. An interface method may be provided to turn it back on.
+ Database modules that do not support transactions should implement this method with void functionality.
+ """
+ self.messages = []
+ if not self.supportsTransactions:
+ return
+
+ try:
+ self.transaction_level = self.connector.CommitTrans()
+ if verbose > 1:
+ print('commit done on connection at %X' % id(self))
+ if not (self._autocommit or (self.connector.Attributes & adc.adXactAbortRetaining)):
+ #If attributes has adXactCommitRetaining it performs retaining commits that is,
+ #calling CommitTrans automatically starts a new transaction. Not all providers support this.
+ #If not, we will have to start a new transaction by this command:
+ self.transaction_level = self.connector.BeginTrans()
+ except Exception as e:
+ self._raiseConnectionError(api.ProgrammingError, e)
+
+ def _rollback(self):
+ """In case a database does provide transactions this method causes the the database to roll back to
+ the start of any pending transaction. Closing a connection without committing the changes first will
+ cause an implicit rollback to be performed.
+
+ If the database does not support the functionality required by the method, the interface should
+ throw an exception in case the method is used.
+ The preferred approach is to not implement the method and thus have Python generate
+ an AttributeError in case the method is requested. This allows the programmer to check for database
+ capabilities using the standard hasattr() function.
+
+ For some dynamically configured interfaces it may not be appropriate to require dynamically making
+ the method available. These interfaces should then raise a NotSupportedError to indicate the
+ non-ability to perform the roll back when the method is invoked.
+ """
+ self.messages=[]
+ if self.transaction_level: # trying to roll back with no open transaction causes an error
+ try:
+ self.transaction_level = self.connector.RollbackTrans()
+ if verbose > 1:
+ print('rollback done on connection at %X' % id(self))
+ if not self._autocommit and not(self.connector.Attributes & adc.adXactAbortRetaining):
+ #If attributes has adXactAbortRetaining it performs retaining aborts that is,
+ #calling RollbackTrans automatically starts a new transaction. Not all providers support this.
+ #If not, we will have to start a new transaction by this command:
+ if self.transaction_level == 0:
+ self.transaction_level = self.connector.BeginTrans()
+ except Exception as e:
+ self._raiseConnectionError(api.ProgrammingError, e)
+
+ def __setattr__(self, name, value):
+ if name == 'autocommit': # extension: allow user to turn autocommit on or off
+ if self.supportsTransactions:
+ object.__setattr__(self, '_autocommit', bool(value))
+ try: self._rollback() # must clear any outstanding transactions
+ except: pass
+ return
+ elif name == 'paramstyle':
+ if value not in api.accepted_paramstyles:
+ self._raiseConnectionError(api.NotSupportedError,
+ 'paramstyle="%s" not in:%s' % (value, repr(api.accepted_paramstyles)))
+ elif name == 'variantConversions':
+ value = copy.copy(value) # make a new copy -- no changes in the default, please
+ object.__setattr__(self, name, value)
+
+ def __getattr__(self, item):
+ if item == 'rollback': # the rollback method only appears if the database supports transactions
+ if self.supportsTransactions:
+ return self._rollback # return the rollback method so the caller can execute it.
+ else:
+ raise AttributeError ('this data provider does not support Rollback')
+ elif item == 'autocommit':
+ return self._autocommit
+ else:
+ raise AttributeError('no such attribute in ADO connection object as="%s"' % item)
+
+ def cursor(self):
+ "Return a new Cursor Object using the connection."
+ self.messages = []
+ c = Cursor(self)
+ return c
+
+ def _i_am_here(self, crsr):
+ "message from a new cursor proclaiming its existence"
+ i = next(self.cursor_counter)
+ self.cursors[i] = crsr
+ crsr.id = i
+
+ def _i_am_closing(self,crsr):
+ "message from a cursor giving connection a chance to clean up"
+ try:
+ del self.cursors[crsr.id]
+ except:
+ pass
+
+ def printADOerrors(self):
+ j=self.connector.Errors.Count
+ if j:
+ print('ADO Errors:(%i)' % j)
+ for e in self.connector.Errors:
+ print('Description: %s' % e.Description)
+ print('Error: %s %s ' % (e.Number, adc.adoErrors.get(e.Number, "unknown")))
+ if e.Number == adc.ado_error_TIMEOUT:
+ print('Timeout Error: Try using adodbpi.connect(constr,timeout=Nseconds)')
+ print('Source: %s' % e.Source)
+ print('NativeError: %s' % e.NativeError)
+ print('SQL State: %s' % e.SQLState)
+
+ def _suggest_error_class(self):
+ """Introspect the current ADO Errors and determine an appropriate error class.
+
+ Error.SQLState is a SQL-defined error condition, per the SQL specification:
+ http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt
+
+ The 23000 class of errors are integrity errors.
+ Error 40002 is a transactional integrity error.
+ """
+ if self.connector is not None:
+ for e in self.connector.Errors:
+ state = str(e.SQLState)
+ if state.startswith('23') or state=='40002':
+ return api.IntegrityError
+ return api.DatabaseError
+
+ def __del__(self):
+ try:
+ self._closeAdoConnection() #v2.1 Rose
+ except:
+ pass
+ self.connector = None
+
+ def __enter__(self): # Connections are context managers
+ return(self)
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ if exc_type:
+ self._rollback() #automatic rollback on errors
+ else:
+ self.commit()
+
+ def get_table_names(self):
+ schema = self.connector.OpenSchema(20) # constant = adSchemaTables
+
+ tables = []
+ while not schema.EOF:
+ name = getIndexedValue(schema.Fields,'TABLE_NAME').Value
+ tables.append(name)
+ schema.MoveNext()
+ del schema
+ return tables
+
+# # # # # ----- the Class that defines a cursor ----- # # # # #
+class Cursor(object):
+## ** api required attributes:
+## description...
+## This read-only attribute is a sequence of 7-item sequences.
+## Each of these sequences contains information describing one result column:
+## (name, type_code, display_size, internal_size, precision, scale, null_ok).
+## This attribute will be None for operations that do not return rows or if the
+## cursor has not had an operation invoked via the executeXXX() method yet.
+## The type_code can be interpreted by comparing it to the Type Objects specified in the section below.
+## rowcount...
+## This read-only attribute specifies the number of rows that the last executeXXX() produced
+## (for DQL statements like select) or affected (for DML statements like update or insert).
+## The attribute is -1 in case no executeXXX() has been performed on the cursor or
+## the rowcount of the last operation is not determinable by the interface.[7]
+## arraysize...
+## This read/write attribute specifies the number of rows to fetch at a time with fetchmany().
+## It defaults to 1 meaning to fetch a single row at a time.
+## Implementations must observe this value with respect to the fetchmany() method,
+## but are free to interact with the database a single row at a time.
+## It may also be used in the implementation of executemany().
+## ** extension attributes:
+## paramstyle...
+## allows the programmer to override the connection's default paramstyle
+## errorhandler...
+## allows the programmer to override the connection's default error handler
+
+ def __init__(self,connection):
+ self.command = None
+ self._ado_prepared = False
+ self.messages=[]
+ self.connection = connection
+ self.paramstyle = connection.paramstyle # used for overriding the paramstyle
+ self._parameter_names = []
+ self.recordset_is_remote = False
+ self.rs = None # the ADO recordset for this cursor
+ self.converters = [] # conversion function for each column
+ self.columnNames = {} # names of columns {lowercase name : number,...}
+ self.numberOfColumns = 0
+ self._description = None
+ self.rowcount = -1
+ self.errorhandler = connection.errorhandler
+ self.arraysize = 1
+ connection._i_am_here(self)
+ if verbose:
+ print('%s New cursor at %X on conn %X' % (version, id(self), id(self.connection)))
+
+ def __iter__(self): # [2.1 Zamarev]
+ return iter(self.fetchone, None) # [2.1 Zamarev]
+
+ def prepare(self, operation):
+ self.command = operation
+ self._description = None
+ self._ado_prepared = 'setup'
+
+ def __next__(self):
+ r = self.fetchone()
+ if r:
+ return r
+ raise StopIteration
+
+ def __enter__(self):
+ "Allow database cursors to be used with context managers."
+ return self
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ "Allow database cursors to be used with context managers."
+ self.close()
+
+ def _raiseCursorError(self, errorclass, errorvalue):
+ eh = self.errorhandler
+ if eh is None:
+ eh = api.standardErrorHandler
+ eh(self.connection, self, errorclass, errorvalue)
+
+ def build_column_info(self, recordset):
+ self.converters = [] # convertion function for each column
+ self.columnNames = {} # names of columns {lowercase name : number,...}
+ self._description = None
+
+ # if EOF and BOF are true at the same time, there are no records in the recordset
+ if (recordset is None) or (recordset.State == adc.adStateClosed):
+ self.rs = None
+ self.numberOfColumns = 0
+ return
+ self.rs = recordset #v2.1.1 bkline
+ self.recordset_format = api.RS_ARRAY if api.onIronPython else api.RS_WIN_32
+ self.numberOfColumns = recordset.Fields.Count
+ try:
+ varCon = self.connection.variantConversions
+ except AttributeError:
+ varCon = api.variantConversions
+ for i in range(self.numberOfColumns):
+ f = getIndexedValue(self.rs.Fields, i)
+ try:
+ self.converters.append(varCon[f.Type]) # conversion function for this column
+ except KeyError:
+ self._raiseCursorError(api.InternalError, 'Data column of Unknown ADO type=%s' % f.Type)
+ self.columnNames[f.Name.lower()] = i # columnNames lookup
+
+ def _makeDescriptionFromRS(self):
+ # Abort if closed or no recordset.
+ if self.rs is None:
+ self._description = None
+ return
+ desc = []
+ for i in range(self.numberOfColumns):
+ f = getIndexedValue(self.rs.Fields, i)
+ if self.rs.EOF or self.rs.BOF:
+ display_size=None
+ else:
+ display_size=f.ActualSize #TODO: Is this the correct defintion according to the DB API 2 Spec ?
+ null_ok= bool(f.Attributes & adc.adFldMayBeNull) #v2.1 Cole
+ desc.append((f.Name, f.Type, display_size, f.DefinedSize, f.Precision, f.NumericScale, null_ok))
+ self._description = desc
+
+ def get_description(self):
+ if not self._description:
+ self._makeDescriptionFromRS()
+ return self._description
+
+ def __getattr__(self, item):
+ if item == 'description':
+ return self.get_description()
+ getattr(self, item) # may get here on Remote attribute calls for existing attributes
+
+ def format_description(self,d):
+ """Format db_api description tuple for printing."""
+ if self.description is None:
+ self._makeDescriptionFromRS()
+ if isinstance(d,int):
+ d = self.description[d]
+ desc = "Name= %s, Type= %s, DispSize= %s, IntSize= %s, Precision= %s, Scale= %s NullOK=%s" % \
+ (d[0], adc.adTypeNames.get(d[1], str(d[1])+' (unknown type)'),
+ d[2], d[3], d[4], d[5], d[6])
+ return desc
+
+ def close(self, dont_tell_me=False):
+ """Close the cursor now (rather than whenever __del__ is called).
+ The cursor will be unusable from this point forward; an Error (or subclass)
+ exception will be raised if any operation is attempted with the cursor.
+ """
+ if self.connection is None:
+ return
+ if not dont_tell_me:
+ self.connection._i_am_closing(self) # take me off the connection's cursors list
+ self.messages = []
+ if self.rs and self.rs.State != adc.adStateClosed: # rs exists and is open #v2.1 Rose
+ self.rs.Close() #v2.1 Rose
+ self.rs = None # let go of the recordset so ADO will let it be disposed #v2.1 Rose
+ self.connection = None #this will make all future method calls on me throw an exception
+ if verbose:
+ print('adodbapi Closed cursor at %X' % id(self))
+
+ def __del__(self):
+ try:
+ self.close()
+ except:
+ pass
+
+ def _new_command(self, command_text, command_type=adc.adCmdText):
+ self.cmd = None
+ self.messages = []
+
+ if self.connection is None:
+ self._raiseCursorError(api.InterfaceError, None)
+ return
+ try:
+ self.cmd = Dispatch("ADODB.Command")
+ self.cmd.ActiveConnection = self.connection.connector
+ self.cmd.CommandTimeout = self.connection.timeout
+ self.cmd.CommandType = command_type
+ self.cmd.CommandText = command_text
+ self.cmd.Prepared = bool(self._ado_prepared)
+ except:
+ self._raiseCursorError(api.DatabaseError,
+ 'Error creating new ADODB.Command object for "%s"' % repr(command_text))
+
+ def _execute_command(self):
+ # Stored procedures may have an integer return value
+ self.return_value = None
+ recordset = None
+ count = -1 #default value
+ if verbose:
+ print('Executing command="%s"'%self.command)
+ try:
+ # ----- the actual SQL is executed here ---
+ if api.onIronPython:
+ ra = Reference[int]()
+ recordset = self.cmd.Execute(ra)
+ count = ra.Value
+ else: #pywin32
+ recordset, count = self.cmd.Execute()
+ # ----- ------------------------------- ---
+ except (Exception) as e:
+ _message = ""
+ if hasattr(e, 'args'): _message += str(e.args)+"\n"
+ _message += "Command:\n%s\nParameters:\n%s" % (self.cmd.CommandText,
+ format_parameters(self.cmd.Parameters, True))
+ klass = self.connection._suggest_error_class()
+ self._raiseCursorError(klass, _message)
+ try:
+ self.rowcount = recordset.RecordCount
+ except:
+ self.rowcount = count
+ self.build_column_info(recordset)
+
+ # The ADO documentation hints that obtaining the recordcount may be timeconsuming
+ # "If the Recordset object does not support approximate positioning, this property
+ # may be a significant drain on resources # [ekelund]
+ # Therefore, COM will not return rowcount for server-side cursors. [Cole]
+ # Client-side cursors (the default since v2.8) will force a static
+ # cursor, and rowcount will then be set accurately [Cole]
+ def get_rowcount(self):
+ return self.rowcount
+
+ def get_returned_parameters(self):
+ """with some providers, returned parameters and the .return_value are not available until
+ after the last recordset has been read. In that case, you must coll nextset() until it
+ returns None, then call this method to get your returned information."""
+
+ retLst=[] # store procedures may return altered parameters, including an added "return value" item
+ for p in tuple(self.cmd.Parameters):
+ if verbose > 2:
+ print('Returned=Name: %s, Dir.: %s, Type: %s, Size: %s, Value: "%s",' \
+ " Precision: %s, NumericScale: %s" % \
+ (p.Name, adc.directions[p.Direction],
+ adc.adTypeNames.get(p.Type, str(p.Type)+' (unknown type)'),
+ p.Size, p.Value, p.Precision, p.NumericScale))
+ pyObject = api.convert_to_python(p.Value, api.variantConversions[p.Type])
+ if p.Direction == adc.adParamReturnValue:
+ self.returnValue = pyObject # also load the undocumented attribute (Vernon's Error!)
+ self.return_value = pyObject
+ else:
+ retLst.append(pyObject)
+ return retLst # return the parameter list to the caller
+
+ def callproc(self, procname, parameters=None):
+ """Call a stored database procedure with the given name.
+ The sequence of parameters must contain one entry for each
+ argument that the sproc expects. The result of the
+ call is returned as modified copy of the input
+ sequence. Input parameters are left untouched, output and
+ input/output parameters replaced with possibly new values.
+
+ The sproc may also provide a result set as output,
+ which is available through the standard .fetch*() methods.
+ Extension: A "return_value" property may be set on the
+ cursor if the sproc defines an integer return value.
+ """
+ self._parameter_names = []
+ self._new_command(procname, command_type=adc.adCmdStoredProc)
+ self._buildADOparameterList(parameters, sproc=True)
+ if verbose > 2:
+ print('Calling Stored Proc with Params=', format_parameters(self.cmd.Parameters, True))
+ self._execute_command()
+ return self.get_returned_parameters()
+
+ def _reformat_operation(self, operation, parameters):
+ if self.paramstyle in ('format', 'pyformat'): # convert %s to ?
+ operation, self._parameter_names = api.changeFormatToQmark(operation)
+ elif self.paramstyle == 'named' or (self.paramstyle == 'dynamic' and isinstance(parameters, Mapping)):
+ operation, self._parameter_names = api.changeNamedToQmark(operation) # convert :name to ?
+ return operation
+
+ def _buildADOparameterList(self, parameters, sproc=False):
+ self.parameters = parameters
+ if parameters is None:
+ parameters = []
+
+ parameters_known = False
+ if sproc: # needed only if we are calling a stored procedure
+ try: # attempt to use ADO's parameter list
+ self.cmd.Parameters.Refresh()
+ if verbose > 2:
+ print('ADO detected Params=', format_parameters(self.cmd.Parameters, True))
+ print('Program Parameters=', repr(parameters))
+ parameters_known = True
+ except api.Error:
+ if verbose:
+ print('ADO Parameter Refresh failed')
+ pass
+ else:
+ if len(parameters) != self.cmd.Parameters.Count - 1:
+ raise api.ProgrammingError('You must supply %d parameters for this stored procedure' % \
+ (self.cmd.Parameters.Count - 1))
+ if sproc or parameters != []:
+ i = 0
+ if parameters_known: # use ado parameter list
+ if self._parameter_names: # named parameters
+ for i, pm_name in enumerate(self._parameter_names):
+ p = getIndexedValue(self.cmd.Parameters, i)
+ try:
+ _configure_parameter(p, parameters[pm_name], p.Type, parameters_known)
+ except (Exception) as e:
+ _message = 'Error Converting Parameter %s: %s, %s <- %s\n' % \
+ (p.Name, adc.ado_type_name(p.Type), p.Value, repr(parameters[pm_name]))
+ self._raiseCursorError(api.DataError, _message+'->'+repr(e.args))
+ else: # regular sequence of parameters
+ for value in parameters:
+ p = getIndexedValue(self.cmd.Parameters,i)
+ if p.Direction == adc.adParamReturnValue: # this is an extra parameter added by ADO
+ i += 1 # skip the extra
+ p=getIndexedValue(self.cmd.Parameters,i)
+ try:
+ _configure_parameter(p, value, p.Type, parameters_known)
+ except (Exception) as e:
+ _message = 'Error Converting Parameter %s: %s, %s <- %s\n' % \
+ (p.Name, adc.ado_type_name(p.Type), p.Value, repr(value))
+ self._raiseCursorError(api.DataError, _message+'->'+repr(e.args))
+ i += 1
+ else: #-- build own parameter list
+ if self._parameter_names: # we expect a dictionary of parameters, this is the list of expected names
+ for parm_name in self._parameter_names:
+ elem = parameters[parm_name]
+ adotype = api.pyTypeToADOType(elem)
+ p = self.cmd.CreateParameter(parm_name, adotype, adc.adParamInput)
+ _configure_parameter(p, elem, adotype, parameters_known)
+ try:
+ self.cmd.Parameters.Append(p)
+ except (Exception) as e:
+ _message = 'Error Building Parameter %s: %s, %s <- %s\n' % \
+ (p.Name, adc.ado_type_name(p.Type), p.Value, repr(elem))
+ self._raiseCursorError(api.DataError, _message+'->'+repr(e.args))
+ else : # expecting the usual sequence of parameters
+ if sproc:
+ p = self.cmd.CreateParameter('@RETURN_VALUE', adc.adInteger, adc.adParamReturnValue)
+ self.cmd.Parameters.Append(p)
+
+ for elem in parameters:
+ name='p%i' % i
+ adotype = api.pyTypeToADOType(elem)
+ p=self.cmd.CreateParameter(name, adotype, adc.adParamInput) # Name, Type, Direction, Size, Value
+ _configure_parameter(p, elem, adotype, parameters_known)
+ try:
+ self.cmd.Parameters.Append(p)
+ except (Exception) as e:
+ _message = 'Error Building Parameter %s: %s, %s <- %s\n' % \
+ (p.Name, adc.ado_type_name(p.Type), p.Value, repr(elem))
+ self._raiseCursorError(api.DataError, _message+'->'+repr(e.args))
+ i += 1
+ if self._ado_prepared == 'setup':
+ self._ado_prepared = True # parameters will be known next loop
+
+ def execute(self, operation, parameters=None):
+ """Prepare and execute a database operation (query or command).
+
+ Parameters may be provided as sequence or mapping and will be bound to variables in the operation.
+ Variables are specified in a database-specific notation
+ (see the module's paramstyle attribute for details). [5]
+ A reference to the operation will be retained by the cursor.
+ If the same operation object is passed in again, then the cursor
+ can optimize its behavior. This is most effective for algorithms
+ where the same operation is used, but different parameters are bound to it (many times).
+
+ For maximum efficiency when reusing an operation, it is best to use
+ the setinputsizes() method to specify the parameter types and sizes ahead of time.
+ It is legal for a parameter to not match the predefined information;
+ the implementation should compensate, possibly with a loss of efficiency.
+
+ The parameters may also be specified as list of tuples to e.g. insert multiple rows in
+ a single operation, but this kind of usage is depreciated: executemany() should be used instead.
+
+ Return value is not defined.
+
+ [5] The module will use the __getitem__ method of the parameters object to map either positions
+ (integers) or names (strings) to parameter values. This allows for both sequences and mappings
+ to be used as input.
+ The term "bound" refers to the process of binding an input value to a database execution buffer.
+ In practical terms, this means that the input value is directly used as a value in the operation.
+ The client should not be required to "escape" the value so that it can be used -- the value
+ should be equal to the actual database value. """
+ if self.command is not operation or self._ado_prepared == 'setup':
+ if self.command is not operation:
+ self._ado_prepared = False
+ self.command = operation
+ self._parameter_names = []
+ if parameters and self.paramstyle != 'qmark':
+ operation = self._reformat_operation(operation, parameters) # if 'named' will set self._parameter_names
+ self._new_command(operation)
+ self._buildADOparameterList(parameters)
+ if verbose > 3:
+ print('Params=', format_parameters(self.cmd.Parameters, True))
+ self._execute_command()
+
+ def executemany(self, operation, seq_of_parameters):
+ """Prepare a database operation (query or command)
+ and then execute it against all parameter sequences or mappings found in the sequence seq_of_parameters.
+
+ Return values are not defined.
+ """
+ self.messages = list()
+ total_recordcount = 0
+
+ self.prepare(operation)
+ for params in seq_of_parameters:
+ self.execute(self.command, params)
+ if self.rowcount == -1:
+ total_recordcount = -1
+ if total_recordcount != -1:
+ total_recordcount += self.rowcount
+ self.rowcount = total_recordcount
+
+ def _fetch(self, limit=None):
+ """Fetch rows from the current recordset.
+
+ limit -- Number of rows to fetch, or None (default) to fetch all rows.
+ """
+ if self.connection is None or self.rs is None:
+ self._raiseCursorError(api.FetchFailedError, 'fetch() on closed connection or empty query set')
+ return
+
+ if self.rs.State == adc.adStateClosed or self.rs.BOF or self.rs.EOF:
+ return list()
+ if limit: # limit number of rows retrieved
+ ado_results = self.rs.GetRows(limit)
+ else: # get all rows
+ ado_results = self.rs.GetRows()
+ if self.recordset_format == api.RS_ARRAY: # result of GetRows is a two-dimension array
+ length = len(ado_results) // self.numberOfColumns # length of first dimension
+ else: #pywin32
+ length = len(ado_results[0]) #result of GetRows is tuples in a tuple
+ fetchObject = api.SQLrows(ado_results, length, self) # new object to hold the results of the fetch
+ return fetchObject
+
+ def fetchone(self):
+ """ Fetch the next row of a query result set, returning a single sequence,
+ or None when no more data is available.
+
+ An Error (or subclass) exception is raised if the previous call to executeXXX()
+ did not produce any result set or no call was issued yet.
+ """
+ self.messages = []
+ result = self._fetch(1)
+ if result: # return record (not list of records)
+ return result[0]
+ return None
+
+ def fetchmany(self, size=None):
+ """Fetch the next set of rows of a query result, returning a list of tuples. An empty sequence is returned when no more rows are available.
+
+ The number of rows to fetch per call is specified by the parameter.
+ If it is not given, the cursor's arraysize determines the number of rows to be fetched.
+ The method should try to fetch as many rows as indicated by the size parameter.
+ If this is not possible due to the specified number of rows not being available,
+ fewer rows may be returned.
+
+ An Error (or subclass) exception is raised if the previous call to executeXXX()
+ did not produce any result set or no call was issued yet.
+
+ Note there are performance considerations involved with the size parameter.
+ For optimal performance, it is usually best to use the arraysize attribute.
+ If the size parameter is used, then it is best for it to retain the same value from
+ one fetchmany() call to the next.
+ """
+ self.messages=[]
+ if size is None:
+ size = self.arraysize
+ return self._fetch(size)
+
+ def fetchall(self):
+ """Fetch all (remaining) rows of a query result, returning them as a sequence of sequences (e.g. a list of tuples).
+
+ Note that the cursor's arraysize attribute
+ can affect the performance of this operation.
+ An Error (or subclass) exception is raised if the previous call to executeXXX()
+ did not produce any result set or no call was issued yet.
+ """
+ self.messages=[]
+ return self._fetch()
+
+ def nextset(self):
+ """Skip to the next available recordset, discarding any remaining rows from the current recordset.
+
+ If there are no more sets, the method returns None. Otherwise, it returns a true
+ value and subsequent calls to the fetch methods will return rows from the next result set.
+
+ An Error (or subclass) exception is raised if the previous call to executeXXX()
+ did not produce any result set or no call was issued yet.
+ """
+ self.messages=[]
+ if self.connection is None or self.rs is None:
+ self._raiseCursorError(api.OperationalError, ('nextset() on closed connection or empty query set'))
+ return None
+
+ if api.onIronPython:
+ try:
+ recordset = self.rs.NextRecordset()
+ except TypeError:
+ recordset = None
+ except api.Error as exc:
+ self._raiseCursorError(api.NotSupportedError, exc.args)
+ else: #pywin32
+ try: #[begin 2.1 ekelund]
+ rsTuple=self.rs.NextRecordset() #
+ except pywintypes.com_error as exc: # return appropriate error
+ self._raiseCursorError(api.NotSupportedError, exc.args)#[end 2.1 ekelund]
+ recordset = rsTuple[0]
+ if recordset is None:
+ return None
+ self.build_column_info(recordset)
+ return True
+
+ def setinputsizes(self,sizes):
+ pass
+
+ def setoutputsize(self, size, column=None):
+ pass
+
+ def _last_query(self): # let the programmer see what query we actually used
+ try:
+ if self.parameters == None:
+ ret = self.cmd.CommandText
+ else:
+ ret = "%s,parameters=%s" % (self.cmd.CommandText,repr(self.parameters))
+ except:
+ ret = None
+ return ret
+ query = property(_last_query, None, None,
+ "returns the last query executed")
+
+if __name__ == '__main__':
+ raise api.ProgrammingError(version + ' cannot be run as a main program.')
diff --git a/Lib/site-packages/adodbapi/examples/db_print.py b/Lib/site-packages/adodbapi/examples/db_print.py
new file mode 100644
index 0000000..338c042
--- /dev/null
+++ b/Lib/site-packages/adodbapi/examples/db_print.py
@@ -0,0 +1,70 @@
+""" db_print.py -- a simple demo for ADO database reads."""
+from __future__ import with_statement #needed for Python 2.5
+
+import sys
+import adodbapi.ado_consts as adc
+
+cmd_args = ('proxy_host', 'proxy_port', 'filename', 'table_name')
+if 'help' in sys.argv:
+ print(('possible settings keywords are:',cmd_args))
+ sys.exit()
+
+kw_args = {} # pick up filename and proxy address from command line (optionally)
+for arg in sys.argv:
+ s = arg.split("=")
+ if len(s) > 1:
+ if s[0] in cmd_args:
+ kw_args[s[0]] = s[1]
+
+kw_args.setdefault('filename', "test.mdb") # assumes server is running from examples folder
+kw_args.setdefault('table_name', 'Products') # the name of the demo table
+
+# the server needs to select the provider based on his Python installation
+provider_switch = ['provider', 'Microsoft.ACE.OLEDB.12.0', "Microsoft.Jet.OLEDB.4.0"]
+
+# ------------------------ START HERE -------------------------------------
+#create the connection
+constr = "Provider=%(provider)s;Data Source=%(filename)s"
+if 'proxy_host' in kw_args:
+ import adodbapi.remote as db
+else:
+ import adodbapi as db
+con = db.connect(constr, kw_args, macro_is64bit=provider_switch)
+
+if kw_args['table_name'] == '?':
+ print('The tables in your database are:')
+ for name in con.get_table_names():
+ print(name)
+else:
+#make a cursor on the connection
+ with con.cursor() as c:
+
+ #run an SQL statement on the cursor
+ sql = 'select * from %s' % kw_args['table_name']
+ print(('performing query="%s"' % sql))
+ c.execute(sql)
+
+ #check the results
+ print(('result rowcount shows as= %d. (Note: -1 means "not known")' \
+ % (c.rowcount,)))
+ print('')
+ print('result data description is:')
+ print(' NAME Type DispSize IntrnlSz Prec Scale Null?')
+ for d in c.description:
+ print((('%16s %-12s %8s %8d %4d %5d %s') % \
+ (d[0], adc.adTypeNames[d[1]], d[2], d[3], d[4],d[5], bool(d[6]))))
+ print('')
+ print('str() of first five records are...')
+
+ #get the results
+ db = c.fetchmany(5)
+
+ #print them
+ for rec in db:
+ print(rec)
+
+ print('')
+ print('repr() of next row is...')
+ print((repr(c.fetchone())))
+ print('')
+con.close()
diff --git a/Lib/site-packages/adodbapi/examples/db_table_names.py b/Lib/site-packages/adodbapi/examples/db_table_names.py
new file mode 100644
index 0000000..f93de0e
--- /dev/null
+++ b/Lib/site-packages/adodbapi/examples/db_table_names.py
@@ -0,0 +1,19 @@
+""" db_table_names.py -- a simple demo for ADO database table listing."""
+import sys
+import adodbapi
+
+try:
+ databasename = sys.argv[1]
+except IndexError:
+ databasename = "test.mdb"
+
+provider = ['prv', "Microsoft.ACE.OLEDB.12.0", "Microsoft.Jet.OLEDB.4.0"]
+constr = "Provider=%(prv)s;Data Source=%(db)s"
+
+#create the connection
+con = adodbapi.connect(constr, db=databasename, macro_is64bit=provider)
+
+print(('Table names in= %s' % databasename))
+
+for table in con.get_table_names():
+ print(table)
diff --git a/Lib/site-packages/adodbapi/examples/xls_read.py b/Lib/site-packages/adodbapi/examples/xls_read.py
new file mode 100644
index 0000000..59fdba5
--- /dev/null
+++ b/Lib/site-packages/adodbapi/examples/xls_read.py
@@ -0,0 +1,38 @@
+import sys
+import adodbapi
+try:
+ import adodbapi.is64bit as is64bit
+ is64 = is64bit.Python()
+except ImportError:
+ is64 = False
+
+if is64:
+ driver = "Microsoft.ACE.OLEDB.12.0"
+else:
+ driver = "Microsoft.Jet.OLEDB.4.0"
+extended = 'Extended Properties="Excel 8.0;HDR=Yes;IMEX=1;"'
+
+try: # first command line argument will be xls file name -- default to the one written by xls_write.py
+ filename = sys.argv[1]
+except IndexError:
+ filename = 'xx.xls'
+
+constr = "Provider=%s;Data Source=%s;%s" % (driver, filename, extended)
+
+conn = adodbapi.connect(constr)
+
+try: # second command line argument will be worksheet name -- default to first worksheet
+ sheet = sys.argv[2]
+except IndexError:
+ # use ADO feature to get the name of the first worksheet
+ sheet = conn.get_table_names()[0]
+
+print(('Shreadsheet=%s Worksheet=%s' % (filename, sheet)))
+print('------------------------------------------------------------')
+crsr = conn.cursor()
+sql = "SELECT * from [%s]" % sheet
+crsr.execute(sql)
+for row in crsr.fetchmany(10):
+ print((repr(row)))
+crsr.close()
+conn.close()
diff --git a/Lib/site-packages/adodbapi/examples/xls_write.py b/Lib/site-packages/adodbapi/examples/xls_write.py
new file mode 100644
index 0000000..66ca5b2
--- /dev/null
+++ b/Lib/site-packages/adodbapi/examples/xls_write.py
@@ -0,0 +1,32 @@
+from __future__ import with_statement # needed only if running Python 2.5
+import adodbapi
+try:
+ import adodbapi.is64bit as is64bit
+ is64 = is64bit.Python()
+except ImportError:
+ is64 = False # in case the user has an old version of adodbapi
+if is64:
+ driver = "Microsoft.ACE.OLEDB.12.0"
+else:
+ driver = "Microsoft.Jet.OLEDB.4.0"
+filename = 'xx.xls' # file will be created if it does not exist
+extended = 'Extended Properties="Excel 8.0;Readonly=False;"'
+
+constr = "Provider=%s;Data Source=%s;%s" % (driver, filename, extended)
+
+conn = adodbapi.connect(constr)
+with conn: # will auto commit if no errors
+ with conn.cursor() as crsr:
+ try: crsr.execute('drop table SheetOne')
+ except: pass # just is case there is one already there
+
+ # create the sheet and the header row and set the types for the columns
+ crsr.execute('create table SheetOne (Header1 text, Header2 text, Header3 text, Header4 text, Header5 text)')
+
+ sql = "INSERT INTO SheetOne (Header1, Header2 ,Header3, Header4, Header5) values (?,?,?,?,?)"
+
+ data = (1, 2, 3, 4, 5)
+ crsr.execute(sql, data) # write the first row of data
+ crsr.execute(sql, (6, 7, 8, 9, 10)) # another row of data
+conn.close()
+print(('Created spreadsheet=%s worksheet=%s' % (filename, 'SheetOne')))
diff --git a/Lib/site-packages/adodbapi/is64bit.py b/Lib/site-packages/adodbapi/is64bit.py
new file mode 100644
index 0000000..bb60360
--- /dev/null
+++ b/Lib/site-packages/adodbapi/is64bit.py
@@ -0,0 +1,33 @@
+"""is64bit.Python() --> boolean value of detected Python word size. is64bit.os() --> os build version"""
+import sys
+
+def Python():
+ if sys.platform == 'cli': #IronPython
+ import System
+ return System.IntPtr.Size == 8
+ else:
+ try:
+ return sys.maxsize > 2147483647
+ except AttributeError:
+ return sys.maxint > 2147483647
+
+def os():
+ import platform
+ pm = platform.machine()
+ if pm != '..' and pm.endswith('64'): # recent Python (not Iron)
+ return True
+ else:
+ import os
+ if 'PROCESSOR_ARCHITEW6432' in os.environ:
+ return True # 32 bit program running on 64 bit Windows
+ try:
+ return os.environ['PROCESSOR_ARCHITECTURE'].endswith('64') # 64 bit Windows 64 bit program
+ except IndexError:
+ pass # not Windows
+ try:
+ return '64' in platform.architecture()[0] # this often works in Linux
+ except:
+ return False # is an older version of Python, assume also an older os (best we can guess)
+
+if __name__ == "__main__":
+ print(("is64bit.Python() =", Python(), "is64bit.os() =", os()))
diff --git a/Lib/site-packages/adodbapi/license.txt b/Lib/site-packages/adodbapi/license.txt
new file mode 100644
index 0000000..c255f4a
--- /dev/null
+++ b/Lib/site-packages/adodbapi/license.txt
@@ -0,0 +1,506 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ , 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
diff --git a/Lib/site-packages/adodbapi/readme.txt b/Lib/site-packages/adodbapi/readme.txt
new file mode 100644
index 0000000..23cdf39
--- /dev/null
+++ b/Lib/site-packages/adodbapi/readme.txt
@@ -0,0 +1,92 @@
+Project
+-------
+adodbapi
+
+A Python DB-API 2.0 (PEP-249) module that makes it easy to use Microsoft ADO
+for connecting with databases and other data sources
+using either CPython or IronPython.
+
+Home page:
+
+Features:
+* 100% DB-API 2.0 (PEP-249) compliant (including most extensions and recommendations).
+* Includes pyunit testcases that describe how to use the module.
+* Fully implemented in Python. -- runs in Python 2.5+ Python 3.0+ and IronPython 2.6+
+* Licensed under the LGPL license, which means that it can be used freely even in commercial programs subject to certain restrictions.
+* Includes SERVER and REMOTE modules so that a Windows proxy can serve ADO databases to a Linux client using PyRO.
+* The user can choose between paramstyles: 'qmark' 'named' 'format' 'pyformat' 'dynamic'
+* Supports data retrieval by column name e.g.:
+ for row in myCurser.execute("select name,age from students"):
+ print("Student", row.name, "is", row.age, "years old.")
+* Supports user-definable system-to-Python data conversion functions (selected by ADO data type, or by column)
+
+Prerequisites:
+* C Python 2.5 or higher
+ and pywin32 (Mark Hammond's python for windows extensions.)
+or
+ Iron Python 2.6 or higher. (works in IPy2.0 for all data types except BUFFER)
+
+Installation:
+* (C-Python on Windows): Download pywin32 from http://sf.net/projects/pywin32 and install from .msi (adodbapi is included)
+* ((to use Windows as a server, also download and install Pyro4 (requires Python 2.6 or later))) https://pypi.python.org/pypi/Pyro4
+* (IronPython on Windows): Download adodbapi from http://sf.net/projects/adodbapi. Unpack the zip.
+ Open a command window as an administrator. CD to the folder containing the unzipped files.
+ Run "setup.py install" using the IronPython of your choice.
+* (Linux, as a client): download and install from PyPi: "pip install adodbapi Pyro4"
+
+NOTE: ...........
+If you do not like the new default operation of returning Numeric columns as decimal.Decimal,
+you can select other options by the user defined conversion feature.
+Try:
+ adodbapi.apibase.variantConversions[adodbapi.ado_consts.adNumeric] = adodbapi.apibase.cvtString
+or:
+ adodbapi.apibase.variantConversions[adodbapi.ado_consts.adNumeric] = adodbapi.apibase.cvtFloat
+or:
+ adodbapi.apibase.variantConversions[adodbapi.ado_consts.adNumeric] = write_your_own_convertion_function
+ ............
+whats new in version 2.6
+ A cursor.prepare() method and support for prepared SQL statements.
+ Lots of refactoring, especially of the Remote and Server modules (still to be treated as Beta code).
+ The quick start document 'quick_reference.odt' will export as a nice-looking pdf.
+ Added paramstyles 'pyformat' and 'dynamic'. If your 'paramstyle' is 'named' you _must_ pass a dictionary of
+ parameters to your .execute() method. If your 'paramstyle' is 'format' 'pyformat' or 'dynamic', you _may_
+ pass a dictionary of parameters -- provided your SQL operation string is formatted correctly.
+
+whats new in version 2.5
+ Remote module: (works on Linux!) allows a Windows computer to serve ADO databases via PyRO
+ Server module: PyRO server for ADO. Run using a command like= C:>python -m adodbapi.server
+ (server has simple connection string macros: is64bit, getuser, sql_provider, auto_security)
+ Brief documentation included. See adodbapi/examples folder adodbapi.rtf
+ New connection method conn.get_table_names() --> list of names of tables in database
+
+ Vastly refactored. Data conversion things have been moved to the new adodbapi.apibase module.
+ Many former module-level attributes are now class attributes. (Should be more thread-safe)
+ Connection objects are now context managers for transactions and will commit or rollback.
+ Cursor objects are context managers and will automatically close themselves.
+ Autocommit can be switched on and off.
+ Keyword and positional arguments on the connect() method work as documented in PEP 249.
+ Keyword arguments from the connect call can be formatted into the connection string.
+ New keyword arguments defined, such as: autocommit, paramstyle, remote_proxy, remote_port.
+ *** Breaking change: variantConversion lookups are simplified: the following will raise KeyError:
+ oldconverter=adodbapi.variantConversions[adodbapi.adoStringTypes]
+ Refactor as: oldconverter=adodbapi.variantConversions[adodbapi.adoStringTypes[0]]
+
+(( More information like this in older_whatsnew.txt ))
+
+License
+-------
+LGPL, see http://www.opensource.org/licenses/lgpl-license.php
+
+Documentation
+-------------
+Start with:
+
+http://www.python.org/topics/database/DatabaseAPI-2.0.html
+read the examples in adodbapi/examples
+and look at the test cases in adodbapi/test directory.
+
+Mailing lists
+-------------
+The adodbapi mailing lists have been deactivated. Submit comments to the
+pywin32 or IronPython mailing lists.
+ -- the bug tracker on sourceforge.net/projects/adodbapi will be checked, (infrequently).
diff --git a/Lib/site-packages/adodbapi/schema_table.py b/Lib/site-packages/adodbapi/schema_table.py
new file mode 100644
index 0000000..5667b9c
--- /dev/null
+++ b/Lib/site-packages/adodbapi/schema_table.py
@@ -0,0 +1,14 @@
+"""call using an open ADO connection --> list of table names"""
+from . import adodbapi
+
+def names(connection_object):
+ ado = connection_object.adoConn
+ schema = ado.OpenSchema(20) # constant = adSchemaTables
+
+ tables = []
+ while not schema.EOF:
+ name = adodbapi.getIndexedValue(schema.Fields,'TABLE_NAME').Value
+ tables.append(name)
+ schema.MoveNext()
+ del schema
+ return tables
diff --git a/Lib/site-packages/adodbapi/test/adodbapitest.py b/Lib/site-packages/adodbapi/test/adodbapitest.py
new file mode 100644
index 0000000..637c3ce
--- /dev/null
+++ b/Lib/site-packages/adodbapi/test/adodbapitest.py
@@ -0,0 +1,1348 @@
+""" Unit tests version 2.6.0.6 for adodbapi"""
+from __future__ import with_statement
+# when Python 2.5 is retired, change that to: from __future__ import print_function
+"""
+ adodbapi - A python DB API 2.0 interface to Microsoft ADO
+
+ Copyright (C) 2002 Henrik Ekelund
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ Updates by Vernon Cole
+"""
+
+import unittest
+import sys
+import datetime
+import decimal
+import copy
+
+try:
+ import win32com.client
+ win32 = True
+except ImportError:
+ win32 = False
+
+# run the configuration module.
+import adodbapitestconfig as config # will set sys.path to find correct version of adodbapi
+# in our code below, all our switches are from config.whatever
+
+import adodbapi
+import adodbapi.apibase as api
+
+
+try:
+ import adodbapi.ado_consts as ado_consts
+except ImportError: #we are doing a shortcut import as a module -- so
+ try:
+ import ado_consts
+ except ImportError:
+ from adodbapi import ado_consts
+
+if sys.version_info >= (3,0):
+ import tryconnection3 as tryconnection
+ def str2bytes(sval):
+ return sval.encode("latin1")
+ str = str
+ long = int
+else:
+ import tryconnection2 as tryconnection
+ def str2bytes(sval):
+ if isinstance(sval, str):
+ return sval
+ return sval.encode("latin1")
+try:
+ bytes
+except NameError:
+ bytes = str
+
+class CommonDBTests(unittest.TestCase):
+ "Self contained super-simple tests in easy syntax, should work on everything between mySQL and Oracle"
+
+ def setUp(self):
+ self.engine = 'unknown'
+
+ def getEngine(self):
+ return self.engine
+
+ def getConnection(self):
+ raise NotImplementedError #"This method must be overriden by a subclass"
+
+ def getCursor(self):
+ return self.getConnection().cursor()
+
+ def testConnection(self):
+ crsr=self.getCursor()
+ assert crsr.__class__.__name__ == 'Cursor'
+
+ def testErrorHandlerInherits(self):
+ if not self.remote:
+ conn=self.getConnection()
+ mycallable=lambda connection,cursor,errorclass,errorvalue: 1
+ conn.errorhandler=mycallable
+ crsr=conn.cursor()
+ assert crsr.errorhandler==mycallable,"Error handler on crsr should be same as on connection"
+
+ def testDefaultErrorHandlerConnection(self):
+ if not self.remote:
+ conn=self.getConnection()
+ del conn.messages[:]
+ try:
+ conn.close()
+ conn.commit() #Should not be able to use connection after it is closed
+ except:
+ assert len(conn.messages)==1
+ assert len(conn.messages[0])==2
+ assert conn.messages[0][0]==api.ProgrammingError
+
+ def testOwnErrorHandlerConnection(self):
+ if self.remote: # ToDo: use "skip"
+ return
+ mycallable=lambda connection,cursor,errorclass,errorvalue: 1 #does not raise anything
+ conn=self.getConnection()
+ conn.errorhandler=mycallable
+ conn.close()
+ conn.commit() #Should not be able to use connection after it is closed
+ assert len(conn.messages)==0
+
+ conn.errorhandler=None #This should bring back the standard error handler
+ try:
+ conn.close()
+ conn.commit() #Should not be able to use connection after it is closed
+ except:
+ pass
+ #The Standard errorhandler appends error to messages attribute
+ assert len(conn.messages)>0,"Setting errorhandler to none should bring back the standard error handler"
+
+
+ def testDefaultErrorHandlerCursor(self):
+ crsr=self.getConnection().cursor()
+ if not self.remote:
+ del crsr.messages[:]
+ try:
+ crsr.execute("SELECT abbtytddrf FROM dasdasd")
+ except:
+ assert len(crsr.messages)==1
+ assert len(crsr.messages[0])==2
+ assert crsr.messages[0][0]==api.DatabaseError
+
+ def testOwnErrorHandlerCursor(self):
+ if self.remote: # ToDo: should be a "skip"
+ return
+ mycallable=lambda connection,cursor,errorclass,errorvalue: 1 #does not raise anything
+ crsr=self.getConnection().cursor()
+ crsr.errorhandler=mycallable
+ crsr.execute("SELECT abbtytddrf FROM dasdasd")
+ assert len(crsr.messages)==0
+
+ crsr.errorhandler=None #This should bring back the standard error handler
+ try:
+ crsr.execute("SELECT abbtytddrf FROM dasdasd")
+ except:
+ pass
+ #The Standard errorhandler appends error to messages attribute
+ assert len(crsr.messages)>0,"Setting errorhandler to none should bring back the standard error handler"
+
+
+ def testUserDefinedConversions(self):
+ if self.remote: ## Todo: should be a "skip"
+ return
+ try:
+ duplicatingConverter=lambda aStringField: aStringField*2
+ assert duplicatingConverter('gabba') == 'gabbagabba'
+
+ self.helpForceDropOnTblTemp()
+ conn=self.getConnection()
+ # the variantConversions attribute should not exist on a normal connection object
+ self.assertRaises(AttributeError, lambda x:conn.variantConversions[x],[2])
+ if not self.remote:
+ # create a variantConversions attribute on the connection
+ conn.variantConversions = copy.copy(api.variantConversions)
+ crsr=conn.cursor()
+ tabdef = "CREATE TABLE xx_%s (fldData VARCHAR(100) NOT NULL, fld2 VARCHAR(20))" % config.tmp
+ crsr.execute(tabdef)
+ crsr.execute("INSERT INTO xx_%s(fldData,fld2) VALUES('gabba','booga')" % config.tmp)
+ crsr.execute("INSERT INTO xx_%s(fldData,fld2) VALUES('hey','yo')" % config.tmp)
+ # change converter for ALL adoStringTypes columns
+ conn.variantConversions[api.adoStringTypes]=duplicatingConverter
+ crsr.execute("SELECT fldData,fld2 FROM xx_%s ORDER BY fldData" % config.tmp)
+
+ rows=crsr.fetchall()
+ row = rows[0]
+ self.assertEquals(row[0],'gabbagabba')
+ row = rows[1]
+ self.assertEquals(row[0],'heyhey')
+ self.assertEquals(row[1],'yoyo')
+
+ upcaseConverter=lambda aStringField: aStringField.upper()
+ assert upcaseConverter('upThis') == 'UPTHIS'
+
+ # now use a single column converter
+ rows.converters[1] = upcaseConverter # convert second column
+ self.assertEquals(row[0],'heyhey') # first will be unchanged
+ self.assertEquals(row[1],'YO') # second will convert to upper case
+
+ finally:
+ try:
+ del conn.variantConversions #Restore the default
+ except: pass
+ self.helpRollbackTblTemp()
+
+ def testUserDefinedConversionForExactNumericTypes(self):
+ # variantConversions is a dictionary of conversion functions
+ # held internally in adodbapi.apibase
+ #
+ # !!! this test intentionally alters the value of what should be constant in the module
+ # !!! no new code should use this example, to is only a test to see that the
+ # !!! deprecated way of doing this still works. (use connection.variantConversions)
+ #
+ if not self.remote and sys.version_info < (3,0): ### Py3 need different test
+ oldconverter = adodbapi.variantConversions[ado_consts.adNumeric] #keep old function to restore later
+ # By default decimal and "numbers" are returned as decimals.
+ # Instead, make numbers return as floats
+ try:
+ adodbapi.variantConversions[ado_consts.adNumeric] = adodbapi.cvtFloat
+ self.helpTestDataType("decimal(18,2)",'NUMBER',3.45,compareAlmostEqual=1)
+ self.helpTestDataType("numeric(18,2)",'NUMBER',3.45,compareAlmostEqual=1)
+ # now return strings
+ adodbapi.variantConversions[ado_consts.adNumeric] = adodbapi.cvtString
+ self.helpTestDataType("numeric(18,2)",'NUMBER','3.45')
+ # now a completly weird user defined convertion
+ adodbapi.variantConversions[ado_consts.adNumeric] = lambda x: '!!This function returns a funny unicode string %s!!'%x
+ self.helpTestDataType("numeric(18,2)",'NUMBER','3.45',
+ allowedReturnValues=['!!This function returns a funny unicode string 3.45!!'])
+ finally:
+ # now reset the converter to its original function
+ adodbapi.variantConversions[ado_consts.adNumeric]=oldconverter #Restore the original convertion function
+
+ def helpTestDataType(self,sqlDataTypeString,
+ DBAPIDataTypeString,
+ pyData,
+ pyDataInputAlternatives=None,
+ compareAlmostEqual=None,
+ allowedReturnValues=None):
+ self.helpForceDropOnTblTemp()
+ conn=self.getConnection()
+ crsr=conn.cursor()
+ tabdef= """
+ CREATE TABLE xx_%s (
+ fldId integer NOT NULL,
+ fldData """ % config.tmp + sqlDataTypeString + ")\n"
+
+ crsr.execute(tabdef)
+
+ #Test Null values mapped to None
+ crsr.execute("INSERT INTO xx_%s (fldId) VALUES (1)" % config.tmp)
+
+ crsr.execute("SELECT fldId,fldData FROM xx_%s" % config.tmp)
+ rs=crsr.fetchone()
+ self.assertEquals(rs[1],None) #Null should be mapped to None
+ assert rs[0]==1
+
+ #Test description related
+ descTuple=crsr.description[1]
+ assert descTuple[0] in ['fldData','flddata'], 'was "%s" expected "%s"'%(descTuple[0],'fldData')
+
+ if DBAPIDataTypeString=='STRING':
+ assert descTuple[1] == api.STRING, 'was "%s" expected "%s"'%(descTuple[1],api.STRING.values)
+ elif DBAPIDataTypeString == 'NUMBER':
+ assert descTuple[1] == api.NUMBER, 'was "%s" expected "%s"'%(descTuple[1],api.NUMBER.values)
+ elif DBAPIDataTypeString == 'BINARY':
+ assert descTuple[1] == api.BINARY, 'was "%s" expected "%s"'%(descTuple[1],api.BINARY.values)
+ elif DBAPIDataTypeString == 'DATETIME':
+ assert descTuple[1] == api.DATETIME, 'was "%s" expected "%s"'%(descTuple[1],api.DATETIME.values)
+ elif DBAPIDataTypeString == 'ROWID':
+ assert descTuple[1] == api.ROWID, 'was "%s" expected "%s"'%(descTuple[1],api.ROWID.values)
+ elif DBAPIDataTypeString == 'UUID':
+ assert descTuple[1] == api.OTHER, 'was "%s" expected "%s"'%(descTuple[1],api.OTHER.values)
+ else:
+ raise NotImplementedError #"DBAPIDataTypeString not provided"
+
+ #Test data binding
+ inputs=[pyData]
+ if pyDataInputAlternatives:
+ inputs.extend(pyDataInputAlternatives)
+ if str is str:
+ inputs = set(inputs) # removes redundant string==unicode tests
+ fldId=1
+ for inParam in inputs:
+ fldId+=1
+ try:
+ crsr.execute("INSERT INTO xx_%s (fldId,fldData) VALUES (?,?)" % config.tmp, (fldId, inParam))
+ except:
+ if self.remote:
+ for message in crsr.messages:
+ print(message)
+ else:
+ conn.printADOerrors()
+ raise
+ crsr.execute("SELECT fldData FROM xx_%s WHERE ?=fldID" % config.tmp, [fldId])
+ rs=crsr.fetchone()
+ if allowedReturnValues:
+ allowedTypes = tuple([type(aRV) for aRV in allowedReturnValues])
+ assert isinstance(rs[0],allowedTypes), \
+ 'result type "%s" must be one of %s'%(type(rs[0]),allowedTypes)
+ else:
+ assert isinstance(rs[0] ,type(pyData)), \
+ 'result type "%s" must be instance of %s'%(type(rs[0]),type(pyData))
+
+ if compareAlmostEqual and DBAPIDataTypeString == 'DATETIME':
+ iso1=adodbapi.dateconverter.DateObjectToIsoFormatString(rs[0])
+ iso2=adodbapi.dateconverter.DateObjectToIsoFormatString(pyData)
+ self.assertEquals(iso1, iso2)
+ elif compareAlmostEqual:
+ s = float(pyData)
+ v = float(rs[0])
+ assert abs(v-s)/s < 0.00001, \
+ "Values not almost equal recvd=%s, expected=%f" %(rs[0],s)
+ else:
+ if allowedReturnValues:
+ ok=False
+ self.assertTrue(rs[0] in allowedReturnValues,
+ 'Value "%s" not in %s' % (repr(rs[0]), allowedReturnValues))
+ else:
+ self.assertEquals(rs[0], pyData,
+ 'Values are not equal recvd="%s", expected="%s"' %(rs[0],pyData))
+
+ def testDataTypeFloat(self):
+ self.helpTestDataType("real",'NUMBER',3.45,compareAlmostEqual=True)
+ self.helpTestDataType("float",'NUMBER',1.79e37,compareAlmostEqual=True)
+
+ def testDataTypeDecmal(self):
+ self.helpTestDataType("decimal(18,2)",'NUMBER',3.45,
+ allowedReturnValues=['3.45','3,45',decimal.Decimal('3.45')])
+ self.helpTestDataType("numeric(18,2)",'NUMBER',3.45,
+ allowedReturnValues=['3.45','3,45',decimal.Decimal('3.45')])
+ self.helpTestDataType("decimal(20,2)",'NUMBER',444444444444444444,
+ allowedReturnValues=['444444444444444444.00', '444444444444444444,00',
+ decimal.Decimal('444444444444444444')])
+ if self.getEngine() == 'MSSQL':
+ self.helpTestDataType("uniqueidentifier",'UUID','{71A4F49E-39F3-42B1-A41E-48FF154996E6}',
+ allowedReturnValues=['{71A4F49E-39F3-42B1-A41E-48FF154996E6}'])
+
+ def testDataTypeMoney(self): #v2.1 Cole -- use decimal for money
+ if self.getEngine() == 'MySQL':
+ self.helpTestDataType("DECIMAL(20,4)",'NUMBER',decimal.Decimal('-922337203685477.5808'))
+ elif self.getEngine() == 'PostgreSQL':
+ self.helpTestDataType("money",'NUMBER',decimal.Decimal('-922337203685477.5808'),
+ compareAlmostEqual=True,
+ allowedReturnValues=[-922337203685477.5808,
+ decimal.Decimal('-922337203685477.5808')])
+ else:
+ self.helpTestDataType("smallmoney",'NUMBER',decimal.Decimal('214748.02'))
+ self.helpTestDataType("money",'NUMBER',decimal.Decimal('-922337203685477.5808'))
+
+ def testDataTypeInt(self):
+ if self.getEngine() != 'PostgreSQL':
+ self.helpTestDataType("tinyint",'NUMBER',115)
+ self.helpTestDataType("smallint",'NUMBER',-32768)
+ if self.getEngine() not in ['ACCESS','PostgreSQL']:
+ self.helpTestDataType("bit",'NUMBER',1) #Does not work correctly with access
+ if self.getEngine() in ['MSSQL','PostgreSQL']:
+ self.helpTestDataType("bigint",'NUMBER',3000000000,
+ allowedReturnValues=[3000000000, int(3000000000)])
+ self.helpTestDataType("int",'NUMBER',2147483647)
+
+ def testDataTypeChar(self):
+ for sqlDataType in ("char(6)","nchar(6)"):
+ self.helpTestDataType(sqlDataType,'STRING','spam ',allowedReturnValues=['spam','spam','spam ','spam '])
+
+ def testDataTypeVarChar(self):
+ if self.getEngine() == 'MySQL':
+ stringKinds = ["varchar(10)","text"]
+ elif self.getEngine() == 'PostgreSQL':
+ stringKinds = ["varchar(10)","text","character varying"]
+ else:
+ stringKinds = ["varchar(10)","nvarchar(10)","text","ntext"] #,"varchar(max)"]
+
+ for sqlDataType in stringKinds:
+ self.helpTestDataType(sqlDataType,'STRING','spam',['spam'])
+
+ def testDataTypeDate(self):
+ if self.getEngine() == 'PostgreSQL':
+ dt = "timestamp"
+ else:
+ dt = "datetime"
+ self.helpTestDataType(dt,'DATETIME',adodbapi.Date(2002,10,28),
+ compareAlmostEqual=True)
+ if self.getEngine() not in ['MySQL','PostgreSQL']:
+ self.helpTestDataType("smalldatetime",'DATETIME',adodbapi.Date(2002,10,28),
+ compareAlmostEqual=True)
+ if tag != 'pythontime' and self.getEngine() not in ['MySQL','PostgreSQL']: # fails when using pythonTime
+ self.helpTestDataType(dt,'DATETIME', adodbapi.Timestamp(2002,10,28,12,15,1),
+ compareAlmostEqual=True)
+
+ def testDataTypeBinary(self):
+ binfld = str2bytes('\x07\x00\xE2\x40*')
+ arv = [binfld, adodbapi.Binary(binfld), bytes(binfld)]
+ if self.getEngine() == 'PostgreSQL':
+ self.helpTestDataType("bytea",'BINARY',adodbapi.Binary(binfld),
+ allowedReturnValues=arv)
+ else:
+ self.helpTestDataType("binary(5)",'BINARY',adodbapi.Binary(binfld),
+ allowedReturnValues=arv)
+ self.helpTestDataType("varbinary(100)",'BINARY',adodbapi.Binary(binfld),
+ allowedReturnValues=arv)
+ if self.getEngine() != 'MySQL':
+ self.helpTestDataType("image",'BINARY',adodbapi.Binary(binfld),
+ allowedReturnValues=arv)
+
+ def helpRollbackTblTemp(self):
+ self.helpForceDropOnTblTemp()
+
+ def helpForceDropOnTblTemp(self):
+ conn=self.getConnection()
+ with conn.cursor() as crsr:
+ try:
+ crsr.execute("DROP TABLE xx_%s" % config.tmp)
+ if not conn.autocommit:
+ conn.commit()
+ except:
+ pass
+
+ def helpCreateAndPopulateTableTemp(self,crsr):
+ tabdef= """
+ CREATE TABLE xx_%s (
+ fldData INTEGER
+ )
+ """ % config.tmp
+ try: #EAFP
+ crsr.execute(tabdef)
+ except api.DatabaseError: # was not dropped before
+ self.helpForceDropOnTblTemp() # so drop it now
+ crsr.execute(tabdef)
+ for i in range(9): # note: this poor SQL code, but a valid test
+ crsr.execute("INSERT INTO xx_%s (fldData) VALUES (%i)" % (config.tmp, i))
+ # NOTE: building the test table without using parameter substitution
+
+ def testFetchAll(self):
+ crsr=self.getCursor()
+ self.helpCreateAndPopulateTableTemp(crsr)
+ crsr.execute("SELECT fldData FROM xx_%s" % config.tmp)
+ rs=crsr.fetchall()
+ assert len(rs)==9
+ #test slice of rows
+ i = 3
+ for row in rs[3:-2]: #should have rowid 3..6
+ assert row[0]==i
+ i+=1
+ self.helpRollbackTblTemp()
+
+ def testPreparedStatement(self):
+ crsr=self.getCursor()
+ self.helpCreateAndPopulateTableTemp(crsr)
+ crsr.prepare("SELECT fldData FROM xx_%s" % config.tmp)
+ crsr.execute(crsr.command)
+ rs=crsr.fetchall()
+ assert len(rs)==9
+ assert rs[2][0]==2
+ self.helpRollbackTblTemp()
+
+ def testWrongPreparedStatement(self):
+ crsr=self.getCursor()
+ self.helpCreateAndPopulateTableTemp(crsr)
+ crsr.prepare("SELECT * FROM nowhere")
+ crsr.execute("SELECT fldData FROM xx_%s" % config.tmp)
+ rs=crsr.fetchall()
+ assert len(rs)==9
+ assert rs[2][0]==2
+ self.helpRollbackTblTemp()
+ def testIterator(self):
+ crsr=self.getCursor()
+ self.helpCreateAndPopulateTableTemp(crsr)
+ crsr.execute("SELECT fldData FROM xx_%s" % config.tmp)
+ for i,row in enumerate(crsr): # using cursor rather than fetchxxx
+ assert row[0]==i
+ self.helpRollbackTblTemp()
+
+ def testExecuteMany(self):
+ crsr=self.getCursor()
+ self.helpCreateAndPopulateTableTemp(crsr)
+ seq_of_values = [ (111,) , (222,) ]
+ crsr.executemany("INSERT INTO xx_%s (fldData) VALUES (?)" % config.tmp, seq_of_values)
+ if crsr.rowcount==-1:
+ print((self.getEngine()+" Provider does not support rowcount (on .executemany())"))
+ else:
+ self.assertEquals( crsr.rowcount,2)
+ crsr.execute("SELECT fldData FROM xx_%s" % config.tmp)
+ rs=crsr.fetchall()
+ assert len(rs)==11
+ self.helpRollbackTblTemp()
+
+
+ def testRowCount(self):
+ crsr=self.getCursor()
+ self.helpCreateAndPopulateTableTemp(crsr)
+ crsr.execute("SELECT fldData FROM xx_%s" % config.tmp)
+ if crsr.rowcount == -1:
+ #print("provider does not support rowcount on select")
+ pass
+ else:
+ self.assertEquals( crsr.rowcount,9)
+ self.helpRollbackTblTemp()
+
+ def testRowCountNoRecordset(self):
+ crsr=self.getCursor()
+ self.helpCreateAndPopulateTableTemp(crsr)
+ crsr.execute("DELETE FROM xx_%s WHERE fldData >= 5" % config.tmp)
+ if crsr.rowcount==-1:
+ print((self.getEngine()+" Provider does not support rowcount (on DELETE)"))
+ else:
+ self.assertEquals( crsr.rowcount,4)
+ self.helpRollbackTblTemp()
+
+ def testFetchMany(self):
+ crsr=self.getCursor()
+ self.helpCreateAndPopulateTableTemp(crsr)
+ crsr.execute("SELECT fldData FROM xx_%s" % config.tmp)
+ rs=crsr.fetchmany(3)
+ assert len(rs)==3
+ rs=crsr.fetchmany(5)
+ assert len(rs)==5
+ rs=crsr.fetchmany(5)
+ assert len(rs)==1 #Ask for five, but there is only one left
+ self.helpRollbackTblTemp()
+
+ def testFetchManyWithArraySize(self):
+ crsr=self.getCursor()
+ self.helpCreateAndPopulateTableTemp(crsr)
+ crsr.execute("SELECT fldData FROM xx_%s" % config.tmp)
+ rs=crsr.fetchmany()
+ assert len(rs)==1 #arraysize Defaults to one
+ crsr.arraysize=4
+ rs=crsr.fetchmany()
+ assert len(rs)==4
+ rs=crsr.fetchmany()
+ assert len(rs)==4
+ rs=crsr.fetchmany()
+ assert len(rs)==0
+ self.helpRollbackTblTemp()
+
+ def testErrorConnect(self):
+ conn = self.getConnection()
+ kw = {}
+ if 'proxy_host' in conn.kwargs:
+ kw['proxy_host'] = conn.kwargs['proxy_host']
+ conn.close()
+ self.assertRaises(api.DatabaseError, self.db, 'not a valid connect string', kw)
+
+ def testRowIterator(self):
+ self.helpForceDropOnTblTemp()
+ conn=self.getConnection()
+ crsr=conn.cursor()
+ tabdef= """
+ CREATE TABLE xx_%s (
+ fldId integer NOT NULL,
+ fldTwo integer,
+ fldThree integer,
+ fldFour integer)
+ """ % config.tmp
+ crsr.execute(tabdef)
+
+ inputs = [(2,3,4),(102,103,104)]
+ fldId=1
+ for inParam in inputs:
+ fldId+=1
+ try:
+ crsr.execute("INSERT INTO xx_%s (fldId,fldTwo,fldThree,fldFour) VALUES (?,?,?,?)" % config.tmp,
+ (fldId,inParam[0],inParam[1],inParam[2]))
+ except:
+ if self.remote:
+ for message in crsr.messages:
+ print(message)
+ else:
+ conn.printADOerrors()
+ raise
+ crsr.execute("SELECT fldTwo,fldThree,fldFour FROM xx_%s WHERE ?=fldID" % config.tmp, [fldId])
+ rec = crsr.fetchone()
+ # check that stepping through an emulated row works
+ for j in range(len(inParam)):
+ assert rec[j] == inParam[j], 'returned value:"%s" != test value:"%s"'%(rec[j],inParam[j])
+ # check that we can get a complete tuple from a row
+ assert tuple(rec) == inParam, 'returned value:"%s" != test value:"%s"'%(repr(rec),repr(inParam))
+ # test that slices of rows work
+ slice1 = tuple(rec[:-1])
+ slice2 = tuple(inParam[0:2])
+ assert slice1 == slice2, 'returned value:"%s" != test value:"%s"'%(repr(slice1),repr(slice2))
+ # now test named column retrieval
+ assert rec['fldTwo'] == inParam[0]
+ assert rec.fldThree == inParam[1]
+ assert rec.fldFour == inParam[2]
+ # test array operation
+ # note that the fields vv vv vv are out of order
+ crsr.execute("select fldThree,fldFour,fldTwo from xx_%s" % config.tmp)
+ recs = crsr.fetchall()
+ assert recs[1][0] == 103
+ assert recs[0][1] == 4
+ assert recs[1]['fldFour'] == 104
+ assert recs[0,0] == 3
+ assert recs[0,'fldTwo'] == 2
+ assert recs[1,2] == 102
+ for i in range(1):
+ for j in range(2):
+ assert recs[i][j] == recs[i,j]
+
+ def testFormatParamstyle(self):
+ self.helpForceDropOnTblTemp()
+ conn=self.getConnection()
+ conn.paramstyle = 'format' #test nonstandard use of paramstyle
+ crsr=conn.cursor()
+ tabdef= """
+ CREATE TABLE xx_%s (
+ fldId integer NOT NULL,
+ fldData varchar(10),
+ fldConst varchar(30))
+ """ % config.tmp
+ crsr.execute(tabdef)
+
+ inputs = ['one','two','three']
+ fldId=2
+ for inParam in inputs:
+ fldId+=1
+ sql = "INSERT INTO xx_" + \
+ config.tmp + \
+ " (fldId,fldConst,fldData) VALUES (%s,'thi%s :may cause? trouble', %s)"
+ try:
+ crsr.execute(sql, (fldId,inParam))
+ except:
+ if self.remote:
+ for message in crsr.messages:
+ print(message)
+ else:
+ conn.printADOerrors()
+ raise
+ crsr.execute("SELECT fldData, fldConst FROM xx_" + config.tmp + " WHERE %s=fldID", [fldId])
+ rec = crsr.fetchone()
+ self.assertEqual(rec[0], inParam, 'returned value:"%s" != test value:"%s"' % (rec[0],inParam))
+ self.assertEqual(rec[1], "thi%s :may cause? trouble")
+
+ # now try an operation with a "%s" as part of a literal
+ sel = "insert into xx_" + config.tmp + " (fldId,fldData) VALUES (%s,'four%sfive')"
+ params = (20,)
+ crsr.execute(sel,params)
+
+ #test the .query implementation
+ assert '(?,' in crsr.query, 'expected:"%s" in "%s"'%('(?,',crsr.query)
+ #test the .command attribute
+ assert crsr.command == sel
+ #test the .parameters attribute
+ if not self.remote: # parameter list will be altered in transit
+ self.assertEqual(crsr.parameters, params)
+ #now make sure the data made it
+ crsr.execute("SELECT fldData FROM xx_%s WHERE fldID=20" % config.tmp)
+ rec = crsr.fetchone()
+ self.assertEqual(rec[0], 'four%sfive')
+
+ def testNamedParamstyle(self):
+ self.helpForceDropOnTblTemp()
+ conn=self.getConnection()
+ crsr=conn.cursor()
+ crsr.paramstyle = 'named' #test nonstandard use of paramstyle
+ tabdef= """
+ CREATE TABLE xx_%s (
+ fldId integer NOT NULL,
+ fldData varchar(10))
+ """ % config.tmp
+ crsr.execute(tabdef)
+
+ inputs = ['four','five','six']
+ fldId=10
+ for inParam in inputs:
+ fldId+=1
+ try:
+ crsr.execute("INSERT INTO xx_%s (fldId,fldData) VALUES (:Id,:f_Val)" % config.tmp,
+ {"f_Val":inParam,'Id':fldId})
+ except:
+ if self.remote:
+ for message in crsr.messages:
+ print(message)
+ else:
+ conn.printADOerrors()
+ raise
+ crsr.execute("SELECT fldData FROM xx_%s WHERE :Id=fldID" % config.tmp, {'Id':fldId})
+ rec = crsr.fetchone()
+ self.assertEqual(rec[0], inParam, 'returned value:"%s" != test value:"%s"'%(rec[0],inParam))
+ # now a test with a ":" as part of a literal
+ crsr.execute("insert into xx_%s (fldId,fldData) VALUES (:xyz,'six:five')" % config.tmp,{'xyz':30})
+ crsr.execute("SELECT fldData FROM xx_%s WHERE fldID=30" % config.tmp)
+ rec = crsr.fetchone()
+ self.assertEqual(rec[0], 'six:five')
+
+
+ def testPyformatParamstyle(self):
+ self.helpForceDropOnTblTemp()
+ conn=self.getConnection()
+ crsr=conn.cursor()
+ crsr.paramstyle = 'pyformat' #test nonstandard use of paramstyle
+ tabdef= """
+ CREATE TABLE xx_%s (
+ fldId integer NOT NULL,
+ fldData varchar(10))
+ """ % config.tmp
+ crsr.execute(tabdef)
+
+ inputs = ['four', 'five', 'six']
+ fldId=10
+ for inParam in inputs:
+ fldId+=1
+ try:
+ crsr.execute("INSERT INTO xx_%s (fldId,fldData) VALUES (%%(Id)s,%%(f_Val)s)" % config.tmp,
+ {"f_Val": inParam, 'Id': fldId})
+ except:
+ if self.remote:
+ for message in crsr.messages:
+ print(message)
+ else:
+ conn.printADOerrors()
+ raise
+ crsr.execute("SELECT fldData FROM xx_%s WHERE %%(Id)s=fldID" % config.tmp, {'Id':fldId})
+ rec = crsr.fetchone()
+ self.assertEqual(rec[0], inParam, 'returned value:"%s" != test value:"%s"'%(rec[0],inParam))
+ # now a test with a "%" as part of a literal
+ crsr.execute("insert into xx_%s (fldId,fldData) VALUES (%%(xyz)s,'six%%five')" % config.tmp,{'xyz': 30})
+ crsr.execute("SELECT fldData FROM xx_%s WHERE fldID=30" % config.tmp)
+ rec = crsr.fetchone()
+ self.assertEqual(rec[0], 'six%five')
+
+ def testAutomaticParamstyle(self):
+ self.helpForceDropOnTblTemp()
+ conn=self.getConnection()
+ conn.paramstyle = 'dynamic' #test nonstandard use of paramstyle
+ crsr=conn.cursor()
+ tabdef= """
+ CREATE TABLE xx_%s (
+ fldId integer NOT NULL,
+ fldData varchar(10),
+ fldConst varchar(30))
+ """ % config.tmp
+ crsr.execute(tabdef)
+ inputs = ['one', 'two', 'three']
+ fldId=2
+ for inParam in inputs:
+ fldId+=1
+ try:
+ crsr.execute("INSERT INTO xx_" + config.tmp + \
+ " (fldId,fldConst,fldData) VALUES (?,'thi%s :may cause? troub:1e', ?)", (fldId,inParam))
+ except:
+ if self.remote:
+ for message in crsr.messages:
+ print(message)
+ else:
+ conn.printADOerrors()
+ raise
+ trouble = 'thi%s :may cause? troub:1e'
+ crsr.execute("SELECT fldData, fldConst FROM xx_" + config.tmp + " WHERE ?=fldID", [fldId])
+ rec = crsr.fetchone()
+ self.assertEqual(rec[0], inParam, 'returned value:"%s" != test value:"%s"'%(rec[0],inParam))
+ self.assertEqual(rec[1], trouble)
+ # inputs = [u'four',u'five',u'six']
+ fldId=10
+ for inParam in inputs:
+ fldId+=1
+ try:
+ crsr.execute("INSERT INTO xx_%s (fldId,fldData) VALUES (:Id,:f_Val)" % config.tmp,
+ {"f_Val":inParam,'Id':fldId})
+ except:
+ if self.remote:
+ for message in crsr.messages:
+ print(message)
+ else:
+ conn.printADOerrors()
+ raise
+ crsr.execute("SELECT fldData FROM xx_%s WHERE :Id=fldID" % config.tmp, {'Id':fldId})
+ rec = crsr.fetchone()
+ self.assertEqual(rec[0], inParam, 'returned value:"%s" != test value:"%s"'%(rec[0],inParam))
+ # now a test with a ":" as part of a literal -- and use a prepared query
+ crsr.prepare("insert into xx_%s (fldId,fldData) VALUES (:xyz,'six:five')" % config.tmp)
+ crsr.execute(crsr.command, {'xyz':30})
+ crsr.execute("SELECT fldData FROM xx_%s WHERE fldID=30" % config.tmp)
+ rec = crsr.fetchone()
+ self.assertEqual(rec[0], 'six:five')
+
+ def testRollBack(self):
+ conn = self.getConnection()
+ crsr = conn.cursor()
+ assert not crsr.connection.autocommit, 'Unexpected beginning condition'
+ self.helpCreateAndPopulateTableTemp(crsr)
+ crsr.connection.commit() # commit the first bunch
+
+ crsr.execute("INSERT INTO xx_%s (fldData) VALUES(100)" % config.tmp)
+
+ selectSql = "SELECT fldData FROM xx_%s WHERE fldData=100" % config.tmp
+ crsr.execute(selectSql)
+ rs = crsr.fetchall()
+ assert len(rs) == 1
+ self.conn.rollback()
+ crsr.execute(selectSql)
+ assert crsr.fetchone() == None, 'cursor.fetchone should return None if a query retrieves no rows'
+ crsr.execute('SELECT fldData from xx_%s' % config.tmp)
+ rs = crsr.fetchall()
+ assert len(rs) == 9, 'the original records should still be present'
+ self.helpRollbackTblTemp()
+
+
+ def testCommit(self):
+ try:
+ con2 = self.getAnotherConnection()
+ except NotImplementedError:
+ return # should be "SKIP" for ACCESS
+ assert not con2.autocommit, 'default should be manual commit'
+ crsr = con2.cursor()
+ self.helpCreateAndPopulateTableTemp(crsr)
+
+ crsr.execute("INSERT INTO xx_%s (fldData) VALUES(100)" % config.tmp)
+ con2.commit()
+
+ selectSql = "SELECT fldData FROM xx_%s WHERE fldData=100" % config.tmp
+ crsr.execute(selectSql)
+ rs = crsr.fetchall()
+ assert len(rs) == 1
+ crsr.close()
+ con2.close()
+ conn = self.getConnection()
+ crsr = self.getCursor()
+ with conn.cursor() as crsr:
+ crsr.execute(selectSql)
+ rs = crsr.fetchall()
+ assert len(rs) == 1
+ assert rs[0][0] == 100
+ self.helpRollbackTblTemp()
+
+
+ def testAutoRollback(self):
+ try:
+ con2 = self.getAnotherConnection()
+ except NotImplementedError:
+ return # should be "SKIP" for ACCESS
+ assert not con2.autocommit, 'unexpected beginning condition'
+ crsr = con2.cursor()
+ self.helpCreateAndPopulateTableTemp(crsr)
+ crsr.execute("INSERT INTO xx_%s (fldData) VALUES(100)" % config.tmp)
+ selectSql = "SELECT fldData FROM xx_%s WHERE fldData=100" % config.tmp
+ crsr.execute(selectSql)
+ rs = crsr.fetchall()
+ assert len(rs) == 1
+ crsr.close()
+ con2.close()
+ crsr = self.getCursor()
+ try:
+ crsr.execute(selectSql) # closing the connection should have forced rollback
+ row = crsr.fetchone()
+ except api.DatabaseError:
+ row = None # if the entire table disappeared the rollback was perfect and the test passed
+ assert row == None, 'cursor.fetchone should return None if a query retrieves no rows. Got %s' % repr(row)
+ self.helpRollbackTblTemp()
+
+ def testAutoCommit(self):
+ try:
+ ac_conn = self.getAnotherConnection({'autocommit': True})
+ except NotImplementedError:
+ return # should be "SKIP" for ACCESS
+ crsr = ac_conn.cursor()
+ self.helpCreateAndPopulateTableTemp(crsr)
+ crsr.execute("INSERT INTO xx_%s (fldData) VALUES(100)" % config.tmp)
+ crsr.close()
+ with self.getCursor() as crsr:
+ selectSql = 'SELECT fldData from xx_%s' % config.tmp
+ crsr.execute(selectSql) # closing the connection should _not_ have forced rollback
+ rs = crsr.fetchall()
+ assert len(rs) == 10, 'all records should still be present'
+ ac_conn.close()
+ self.helpRollbackTblTemp()
+
+ def testSwitchedAutoCommit(self):
+ try:
+ ac_conn = self.getAnotherConnection()
+ except NotImplementedError:
+ return # should be "SKIP" for ACCESS
+ ac_conn.autocommit = True
+ crsr = ac_conn.cursor()
+ self.helpCreateAndPopulateTableTemp(crsr)
+ crsr.execute("INSERT INTO xx_%s (fldData) VALUES(100)" % config.tmp)
+ crsr.close()
+ conn = self.getConnection()
+ ac_conn.close()
+ with self.getCursor() as crsr:
+ selectSql = 'SELECT fldData from xx_%s' % config.tmp
+ crsr.execute(selectSql) # closing the connection should _not_ have forced rollback
+ rs = crsr.fetchall()
+ assert len(rs) == 10, 'all records should still be present'
+ self.helpRollbackTblTemp()
+
+class TestADOwithSQLServer(CommonDBTests):
+ def setUp(self):
+ self.conn = config.dbSqlServerconnect(*config.connStrSQLServer[0], **config.connStrSQLServer[1])
+ self.conn.timeout = 30 # turn timeout back up
+ self.engine = 'MSSQL'
+ self.db = config.dbSqlServerconnect
+ self.remote = config.connStrSQLServer[2]
+
+ def tearDown(self):
+ try:
+ self.conn.rollback()
+ except:
+ pass
+ try:
+ self.conn.close()
+ except:
+ pass
+ self.conn=None
+
+ def getConnection(self):
+ return self.conn
+
+ def getAnotherConnection(self, addkeys=None):
+ keys = dict(config.connStrSQLServer[1])
+ if addkeys:
+ keys.update(addkeys)
+ return config.dbSqlServerconnect(*config.connStrSQLServer[0], **keys)
+
+ def testVariableReturningStoredProcedure(self):
+ crsr=self.conn.cursor()
+ spdef= """
+ CREATE PROCEDURE sp_DeleteMeOnlyForTesting
+ @theInput varchar(50),
+ @theOtherInput varchar(50),
+ @theOutput varchar(100) OUTPUT
+ AS
+ SET @theOutput=@theInput+@theOtherInput
+ """
+ try:
+ crsr.execute("DROP PROCEDURE sp_DeleteMeOnlyForTesting")
+ self.conn.commit()
+ except: #Make sure it is empty
+ pass
+ crsr.execute(spdef)
+
+ retvalues=crsr.callproc('sp_DeleteMeOnlyForTesting',('Dodsworth','Anne',' '))
+ assert retvalues[0]=='Dodsworth', '%s is not "Dodsworth"'%repr(retvalues[0])
+ assert retvalues[1]=='Anne','%s is not "Anne"'%repr(retvalues[1])
+ assert retvalues[2]=='DodsworthAnne','%s is not "DodsworthAnne"'%repr(retvalues[2])
+ self.conn.rollback()
+
+ def testMultipleSetReturn(self):
+ crsr=self.getCursor()
+ self.helpCreateAndPopulateTableTemp(crsr)
+
+ spdef= """
+ CREATE PROCEDURE sp_DeleteMe_OnlyForTesting
+ AS
+ SELECT fldData FROM xx_%s ORDER BY fldData ASC
+ SELECT fldData From xx_%s where fldData = -9999
+ SELECT fldData FROM xx_%s ORDER BY fldData DESC
+ """ % (config.tmp, config.tmp, config.tmp)
+ try:
+ crsr.execute("DROP PROCEDURE sp_DeleteMe_OnlyForTesting")
+ self.conn.commit()
+ except: #Make sure it is empty
+ pass
+ crsr.execute(spdef)
+
+ retvalues=crsr.callproc('sp_DeleteMe_OnlyForTesting')
+ row=crsr.fetchone()
+ self.assertEquals(row[0], 0)
+ assert crsr.nextset() == True, 'Operation should succeed'
+ assert not crsr.fetchall(), 'Should be an empty second set'
+ assert crsr.nextset() == True, 'third set should be present'
+ rowdesc=crsr.fetchall()
+ self.assertEquals(rowdesc[0][0],8)
+ assert crsr.nextset() == None,'No more return sets, should return None'
+
+ self.helpRollbackTblTemp()
+
+
+ def testDatetimeProcedureParameter(self):
+ crsr=self.conn.cursor()
+ spdef= """
+ CREATE PROCEDURE sp_DeleteMeOnlyForTesting
+ @theInput DATETIME,
+ @theOtherInput varchar(50),
+ @theOutput varchar(100) OUTPUT
+ AS
+ SET @theOutput = CONVERT(CHARACTER(20), @theInput, 0) + @theOtherInput
+ """
+ try:
+ crsr.execute("DROP PROCEDURE sp_DeleteMeOnlyForTesting")
+ self.conn.commit()
+ except: #Make sure it is empty
+ pass
+ crsr.execute(spdef)
+
+ result = crsr.callproc('sp_DeleteMeOnlyForTesting', [adodbapi.Timestamp(2014,12,25,0,1,0), 'Beep', ' ' * 30])
+
+ assert result[2] == 'Dec 25 2014 12:01AM Beep', 'value was="%s"' % result[2]
+ self.conn.rollback()
+
+ def testIncorrectStoredProcedureParameter(self):
+ crsr=self.conn.cursor()
+ spdef= """
+ CREATE PROCEDURE sp_DeleteMeOnlyForTesting
+ @theInput DATETIME,
+ @theOtherInput varchar(50),
+ @theOutput varchar(100) OUTPUT
+ AS
+ SET @theOutput = CONVERT(CHARACTER(20), @theInput) + @theOtherInput
+ """
+ try:
+ crsr.execute("DROP PROCEDURE sp_DeleteMeOnlyForTesting")
+ self.conn.commit()
+ except: #Make sure it is empty
+ pass
+ crsr.execute(spdef)
+
+ # calling the sproc with a string for the first parameter where a DateTime is expected
+ result = tryconnection.try_operation_with_expected_exception(
+ (api.DataError,api.DatabaseError),
+ crsr.callproc,
+ ['sp_DeleteMeOnlyForTesting'],
+ {'parameters': ['this is wrong', 'Anne', 'not Alice']}
+ )
+ if result[0]: # the expected exception was raised
+ assert '@theInput' in str(result[1]) or 'DatabaseError' in str(result), \
+ 'Identifies the wrong erroneous parameter'
+ else:
+ assert result[0], result[1] # incorrect or no exception
+ self.conn.rollback()
+
+class TestADOwithAccessDB(CommonDBTests):
+ def setUp(self):
+ self.conn = config.dbAccessconnect(*config.connStrAccess[0], **config.connStrAccess[1])
+ self.conn.timeout = 30 # turn timeout back up
+ self.engine = 'ACCESS'
+ self.db = config.dbAccessconnect
+ self.remote = config.connStrAccess[2]
+
+ def tearDown(self):
+ try:
+ self.conn.rollback()
+ except:
+ pass
+ try:
+ self.conn.close()
+ except:
+ pass
+ self.conn=None
+
+ def getConnection(self):
+ return self.conn
+
+ def getAnotherConnection(self, addkeys=None):
+ raise NotImplementedError('Jet cannot use a second connection to the database')
+
+ def testOkConnect(self):
+ c = self.db(*config.connStrAccess[0], **config.connStrAccess[1])
+ assert c != None
+ c.close()
+
+class TestADOwithMySql(CommonDBTests):
+ def setUp(self):
+ self.conn = config.dbMySqlconnect(*config.connStrMySql[0], **config.connStrMySql[1])
+ self.conn.timeout = 30 # turn timeout back up
+ self.engine = 'MySQL'
+ self.db = config.dbMySqlconnect
+ self.remote = config.connStrMySql[2]
+
+ def tearDown(self):
+ try:
+ self.conn.rollback()
+ except:
+ pass
+ try:
+ self.conn.close()
+ except:
+ pass
+ self.conn=None
+
+ def getConnection(self):
+ return self.conn
+
+ def getAnotherConnection(self, addkeys=None):
+ keys = dict(config.connStrMySql[1])
+ if addkeys:
+ keys.update(addkeys)
+ return config.dbMySqlconnect(*config.connStrMySql[0], **keys)
+
+ def testOkConnect(self):
+ c = self.db(*config.connStrMySql[0], **config.connStrMySql[1])
+ assert c != None
+
+ # def testStoredProcedure(self):
+ # crsr=self.conn.cursor()
+ # try:
+ # crsr.execute("DROP PROCEDURE DeleteMeOnlyForTesting")
+ # self.conn.commit()
+ # except: #Make sure it is empty
+ # pass
+ # spdef= """
+ # DELIMITER $$
+ # CREATE PROCEDURE DeleteMeOnlyForTesting (onein CHAR(10), twoin CHAR(10), OUT theout CHAR(20))
+ # DETERMINISTIC
+ # BEGIN
+ # SET theout = onein //|| twoin;
+ # /* (SELECT 'a small string' as result; */
+ # END $$
+ # """
+ #
+ # crsr.execute(spdef)
+ #
+ # retvalues=crsr.callproc('DeleteMeOnlyForTesting',('Dodsworth','Anne',' '))
+ # print 'return value (mysql)=',repr(crsr.returnValue) ###
+ # assert retvalues[0]=='Dodsworth', '%s is not "Dodsworth"'%repr(retvalues[0])
+ # assert retvalues[1]=='Anne','%s is not "Anne"'%repr(retvalues[1])
+ # assert retvalues[2]=='DodsworthAnne','%s is not "DodsworthAnne"'%repr(retvalues[2])
+ #
+ # try:
+ # crsr.execute("DROP PROCEDURE, DeleteMeOnlyForTesting")
+ # self.conn.commit()
+ # except: #Make sure it is empty
+ # pass
+
+class TestADOwithPostgres(CommonDBTests):
+ def setUp(self):
+ self.conn = config.dbPostgresConnect(*config.connStrPostgres[0], **config.connStrPostgres[1])
+ self.conn.timeout = 30 # turn timeout back up
+ self.engine = 'PostgreSQL'
+ self.db = config.dbPostgresConnect
+ self.remote = config.connStrPostgres[2]
+
+ def tearDown(self):
+ try:
+ self.conn.rollback()
+ except:
+ pass
+ try:
+ self.conn.close()
+ except:
+ pass
+ self.conn=None
+
+ def getConnection(self):
+ return self.conn
+
+ def getAnotherConnection(self, addkeys=None):
+ keys = dict(config.connStrPostgres[1])
+ if addkeys:
+ keys.update(addkeys)
+ return config.dbPostgresConnect(*config.connStrPostgres[0], **keys)
+
+ def testOkConnect(self):
+ c = self.db(*config.connStrPostgres[0], **config.connStrPostgres[1])
+ assert c != None
+
+ # def testStoredProcedure(self):
+ # crsr=self.conn.cursor()
+ # spdef= """
+ # CREATE OR REPLACE FUNCTION DeleteMeOnlyForTesting (text, text)
+ # RETURNS text AS $funk$
+ # BEGIN
+ # RETURN $1 || $2;
+ # END;
+ # $funk$
+ # LANGUAGE SQL;
+ # """
+ #
+ # crsr.execute(spdef)
+ # retvalues = crsr.callproc('DeleteMeOnlyForTesting',('Dodsworth','Anne',' '))
+ # ### print 'return value (pg)=',repr(crsr.returnValue) ###
+ # assert retvalues[0]=='Dodsworth', '%s is not "Dodsworth"'%repr(retvalues[0])
+ # assert retvalues[1]=='Anne','%s is not "Anne"'%repr(retvalues[1])
+ # assert retvalues[2]=='Dodsworth Anne','%s is not "Dodsworth Anne"'%repr(retvalues[2])
+ # self.conn.rollback()
+ # try:
+ # crsr.execute("DROP PROCEDURE, DeleteMeOnlyForTesting")
+ # self.conn.commit()
+ # except: #Make sure it is empty
+ # pass
+
+class TimeConverterInterfaceTest(unittest.TestCase):
+ def testIDate(self):
+ assert self.tc.Date(1990,2,2)
+
+ def testITime(self):
+ assert self.tc.Time(13,2,2)
+
+ def testITimestamp(self):
+ assert self.tc.Timestamp(1990,2,2,13,2,1)
+
+ def testIDateObjectFromCOMDate(self):
+ assert self.tc.DateObjectFromCOMDate(37435.7604282)
+
+ def testICOMDate(self):
+ assert hasattr(self.tc,'COMDate')
+
+ def testExactDate(self):
+ d=self.tc.Date(1994,11,15)
+ comDate=self.tc.COMDate(d)
+ correct=34653.0
+ assert comDate == correct,comDate
+
+ def testExactTimestamp(self):
+ d=self.tc.Timestamp(1994,11,15,12,0,0)
+ comDate=self.tc.COMDate(d)
+ correct=34653.5
+ self.assertEquals( comDate ,correct)
+
+ d=self.tc.Timestamp(2003,5,6,14,15,17)
+ comDate=self.tc.COMDate(d)
+ correct=37747.593946759262
+ self.assertEquals( comDate ,correct)
+
+ def testIsoFormat(self):
+ d=self.tc.Timestamp(1994,11,15,12,3,10)
+ iso=self.tc.DateObjectToIsoFormatString(d)
+ self.assertEquals(str(iso[:19]) , '1994-11-15 12:03:10')
+
+ dt=self.tc.Date(2003,5,2)
+ iso=self.tc.DateObjectToIsoFormatString(dt)
+ self.assertEquals(str(iso[:10]), '2003-05-02')
+
+if config.doMxDateTimeTest:
+ import mx.DateTime
+class TestMXDateTimeConverter(TimeConverterInterfaceTest):
+ def setUp(self):
+ self.tc = api.mxDateTimeConverter()
+
+ def testCOMDate(self):
+ t=mx.DateTime.DateTime(2002,6,28,18,15,2)
+ cmd=self.tc.COMDate(t)
+ assert cmd == t.COMDate()
+
+ def testDateObjectFromCOMDate(self):
+ cmd=self.tc.DateObjectFromCOMDate(37435.7604282)
+ t=mx.DateTime.DateTime(2002,6,28,18,15,0)
+ t2=mx.DateTime.DateTime(2002,6,28,18,15,2)
+ assert t2>cmd>t
+
+ def testDate(self):
+ assert mx.DateTime.Date(1980,11,4)==self.tc.Date(1980,11,4)
+
+ def testTime(self):
+ assert mx.DateTime.Time(13,11,4)==self.tc.Time(13,11,4)
+
+ def testTimestamp(self):
+ t=mx.DateTime.DateTime(2002,6,28,18,15,1)
+ obj=self.tc.Timestamp(2002,6,28,18,15,1)
+ assert t == obj
+
+import time
+class TestPythonTimeConverter(TimeConverterInterfaceTest):
+ def setUp(self):
+ self.tc=api.pythonTimeConverter()
+
+ def testCOMDate(self):
+ mk = time.mktime((2002,6,28,18,15,1, 4,31+28+31+30+31+28,-1))
+ t=time.localtime(mk)
+ # Fri, 28 Jun 2002 18:15:01 +0000
+ cmd=self.tc.COMDate(t)
+ assert abs(cmd - 37435.7604282) < 1.0/24,"%f more than an hour wrong" % cmd
+
+ def testDateObjectFromCOMDate(self):
+ cmd=self.tc.DateObjectFromCOMDate(37435.7604282)
+ t1=time.gmtime(time.mktime((2002,6,28,0,14,1, 4,31+28+31+30+31+28,-1)))
+ #there are errors in the implementation of gmtime which we ignore
+ t2=time.gmtime(time.mktime((2002,6,29,12,14,2, 4,31+28+31+30+31+28,-1)))
+ assert t1= (3,0):
+ import tryconnection3 as tryconnection
+else:
+ import tryconnection2 as tryconnection
+
+print((sys.version))
+node = platform.node()
+try: print(('node=%s: is64bit.os()= %s, is64bit.Python()= %s' % (node, is64bit.os(), is64bit.Python())))
+except: pass
+
+try:
+ onWindows = bool(sys.getwindowsversion()) # seems to work on all versions of Python
+except:
+ onWindows = False
+
+# create a random name for temporary table names
+_alphabet = "PYFGCRLAOEUIDHTNTQJKXBMWVZ1234567890" # yes, I do use a dvorak keyboard!
+tmp = ''.join([random.choice(_alphabet) for x in range(9)])
+mdb_name = 'xx_' + tmp + '.mdb'
+testfolder = setuptestframework.maketemp()
+
+if '--package' in sys.argv:
+ pth = setuptestframework.makeadopackage(testfolder)
+else:
+ pth = setuptestframework.find_ado_path()
+if pth not in sys.path:
+ sys.path.insert(1,pth)
+
+# function to clean up the temporary folder -- calling program must run this function before exit.
+cleanup = setuptestframework.getcleanupfunction()
+
+import adodbapi # will (hopefully) be imported using the "pth" discovered above
+
+try:
+ print((adodbapi.version)) # show version
+except:
+ print('"adodbapi.version" not present or not working.')
+print(__doc__)
+
+verbose = False
+for a in sys.argv:
+ if a.startswith('--verbose'):
+ arg = True
+ try: arg = int(a.split("=")[1])
+ except IndexError: pass
+ adodbapi.adodbapi.verbose = arg
+ verbose = arg
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# # start your environment setup here v v v
+SQL_HOST_NODE = 'Vpad'
+doAllTests = '--all' in sys.argv
+doAccessTest = not ('--nojet' in sys.argv)
+doSqlServerTest = node == SQL_HOST_NODE or '--mssql' in sys.argv or doAllTests
+doMySqlTest = '--mysql' in sys.argv or doAllTests
+doPostgresTest = '--pg' in sys.argv or doAllTests
+iterateOverTimeTests = ('--time' in sys.argv or doAllTests) and onWindows
+
+THE_PROXY_HOST = '25.44.77.176' if node != SQL_HOST_NODE or not onWindows else '::1' # -- change this
+
+try: #If mx extensions are installed, use mxDateTime
+ import mx.DateTime
+ doMxDateTimeTest=True
+except:
+ doMxDateTimeTest=False #Requires eGenixMXExtensions
+
+doTimeTest = True # obsolete python time format
+
+if doAccessTest:
+ if onWindows and (node == SQL_HOST_NODE or not is64bit.Python()):
+ c = {'mdb': setuptestframework.makemdb(testfolder, mdb_name)}
+ else:
+ c = {'macro_find_temp_test_path' : ['mdb', 'server_test.mdb'],
+ 'proxy_host' : THE_PROXY_HOST}
+
+
+ # macro definition for keyword "driver" using macro "is64bit" -- see documentation
+ c['macro_is64bit'] = ['driver', "Microsoft.ACE.OLEDB.12.0", "Microsoft.Jet.OLEDB.4.0"]
+ connStrAccess = "Provider=%(driver)s;Data Source=%(mdb)s"
+ print(' ...Testing ACCESS connection...')
+ doAccessTest, connStrAccess, dbAccessconnect = tryconnection.try_connection(verbose, connStrAccess, 10, **c)
+
+if doSqlServerTest:
+ c = {'macro_getnode' : ['host', r"%s\SQLExpress"], # name of computer with SQL Server
+ #'host':'25.44.77.176;' # Network Library=dbmssocn',
+ 'database': "adotest",
+ 'user' : 'adotestuser', # None implies Windows security
+ 'password' : "12345678",
+ # macro definition for keyword "security" using macro "auto_security"
+ 'macro_auto_security' : 'security',
+ 'provider' : 'SQLNCLI11; MARS Connection=True'
+ }
+ connStr = "Provider=%(provider)s; Initial Catalog=%(database)s; Data Source=%(host)s; %(security)s;"
+
+ if node != SQL_HOST_NODE:
+ if THE_PROXY_HOST:
+ c["proxy_host"] = THE_PROXY_HOST # the SQL server runs a proxy for this test
+ else:
+ c["pyro_connection"] = "PYRONAME:ado.connection"
+ print(' ...Testing MS-SQL login...')
+ doSqlServerTest, connStrSQLServer, dbSqlServerconnect = tryconnection.try_connection(verbose, connStr, 30, **c)
+
+if doMySqlTest:
+ c = {'host' : "25.223.161.222",
+ 'database' : 'test',
+ 'user' : 'adotest',
+ 'password' : '12345678',
+ 'driver' : "MySQL ODBC 5.3 Unicode Driver"} # or _driver="MySQL ODBC 3.51 Driver
+
+ if not onWindows:
+ if THE_PROXY_HOST:
+ c["proxy_host"] = THE_PROXY_HOST
+ else:
+ c["pyro_connection"] = "PYRONAME:ado.connection"
+
+ c['macro_is64bit'] = ['provider', 'Provider=MSDASQL;']
+ cs = '%(provider)sDriver={%(driver)s};Server=%(host)s;Port=3306;' + \
+ 'Database=%(database)s;user=%(user)s;password=%(password)s;Option=3;'
+ print(' ...Testing MySql login...')
+ doMySqlTest, connStrMySql, dbMySqlconnect = tryconnection.try_connection(verbose, cs, 5, **c)
+
+if doPostgresTest:
+ _computername = "25.223.161.222"
+ _databasename='adotest'
+ _username = 'adotestuser'
+ _password = '12345678'
+ kws = {'timeout' : 4}
+ kws['macro_is64bit'] = ['prov_drv', 'Provider=MSDASQL;Driver={PostgreSQL Unicode(x64)}',
+ 'Driver=PostgreSQL Unicode']
+ if not onWindows:
+ if THE_PROXY_HOST:
+ kws['proxy_host'] = THE_PROXY_HOST
+ else:
+ kws['pyro_connection'] = 'PYRONAME:ado.connection'
+ # get driver from http://www.postgresql.org/ftp/odbc/versions/
+ # test using positional and keyword arguments (bad example for real code)
+ print(' ...Testing PostgreSQL login...')
+ doPostgresTest, connStrPostgres, dbPostgresConnect = tryconnection.try_connection(verbose,
+ '%(prov_drv)s;Server=%(host)s;Database=%(database)s;uid=%(user)s;pwd=%(password)s;',
+ _username, _password, _computername, _databasename, **kws)
+
+assert doAccessTest or doSqlServerTest or doMySqlTest or doPostgresTest, 'No database engine found for testing'
diff --git a/Lib/site-packages/adodbapi/test/dbapi20.py b/Lib/site-packages/adodbapi/test/dbapi20.py
new file mode 100644
index 0000000..a3fcd59
--- /dev/null
+++ b/Lib/site-packages/adodbapi/test/dbapi20.py
@@ -0,0 +1,899 @@
+#!/usr/bin/env python
+''' Python DB API 2.0 driver compliance unit test suite.
+
+ This software is Public Domain and may be used without restrictions.
+
+ "Now we have booze and barflies entering the discussion, plus rumours of
+ DBAs on drugs... and I won't tell you what flashes through my mind each
+ time I read the subject line with 'Anal Compliance' in it. All around
+ this is turning out to be a thoroughly unwholesome unit test."
+
+ -- Ian Bicking
+'''
+
+__version__ = '$Revision: 1.14.3 $'[11:-2]
+__author__ = 'Stuart Bishop '
+
+import unittest
+import time
+import sys
+
+if sys.version[0] >= '3': #python 3.x
+ _BaseException = Exception
+ def _failUnless(self, expr, msg=None):
+ self.assertTrue(expr, msg)
+else: #python 2.x
+ from exceptions import Exception as _BaseException
+ def _failUnless(self, expr, msg=None):
+ self.failUnless(expr, msg) ## deprecated since Python 2.6
+
+# set this to "True" to follow API 2.0 to the letter
+TEST_FOR_NON_IDEMPOTENT_CLOSE = True
+
+# Revision 1.14 2013/05/20 11:02:05 kf7xm
+# Add a literal string to the format insertion test to catch trivial re-format algorithms
+
+# Revision 1.13 2013/05/08 14:31:50 kf7xm
+# Quick switch to Turn off IDEMPOTENT_CLOSE test. Also: Silence teardown failure
+
+# Revision 1.12 2009/02/06 03:35:11 kf7xm
+# Tested okay with Python 3.0, includes last minute patches from Mark H.
+#
+# Revision 1.1.1.1.2.1 2008/09/20 19:54:59 rupole
+# Include latest changes from main branch
+# Updates for py3k
+#
+# Revision 1.11 2005/01/02 02:41:01 zenzen
+# Update author email address
+#
+# Revision 1.10 2003/10/09 03:14:14 zenzen
+# Add test for DB API 2.0 optional extension, where database exceptions
+# are exposed as attributes on the Connection object.
+#
+# Revision 1.9 2003/08/13 01:16:36 zenzen
+# Minor tweak from Stefan Fleiter
+#
+# Revision 1.8 2003/04/10 00:13:25 zenzen
+# Changes, as per suggestions by M.-A. Lemburg
+# - Add a table prefix, to ensure namespace collisions can always be avoided
+#
+# Revision 1.7 2003/02/26 23:33:37 zenzen
+# Break out DDL into helper functions, as per request by David Rushby
+#
+# Revision 1.6 2003/02/21 03:04:33 zenzen
+# Stuff from Henrik Ekelund:
+# added test_None
+# added test_nextset & hooks
+#
+# Revision 1.5 2003/02/17 22:08:43 zenzen
+# Implement suggestions and code from Henrik Eklund - test that cursor.arraysize
+# defaults to 1 & generic cursor.callproc test added
+#
+# Revision 1.4 2003/02/15 00:16:33 zenzen
+# Changes, as per suggestions and bug reports by M.-A. Lemburg,
+# Matthew T. Kromer, Federico Di Gregorio and Daniel Dittmar
+# - Class renamed
+# - Now a subclass of TestCase, to avoid requiring the driver stub
+# to use multiple inheritance
+# - Reversed the polarity of buggy test in test_description
+# - Test exception heirarchy correctly
+# - self.populate is now self._populate(), so if a driver stub
+# overrides self.ddl1 this change propogates
+# - VARCHAR columns now have a width, which will hopefully make the
+# DDL even more portible (this will be reversed if it causes more problems)
+# - cursor.rowcount being checked after various execute and fetchXXX methods
+# - Check for fetchall and fetchmany returning empty lists after results
+# are exhausted (already checking for empty lists if select retrieved
+# nothing
+# - Fix bugs in test_setoutputsize_basic and test_setinputsizes
+#
+def str2bytes(sval):
+ if sys.version_info < (3,0) and isinstance(sval, str):
+ sval = sval.decode("latin1")
+ return sval.encode("latin1") #python 3 make unicode into bytes
+
+class DatabaseAPI20Test(unittest.TestCase):
+ ''' Test a database self.driver for DB API 2.0 compatibility.
+ This implementation tests Gadfly, but the TestCase
+ is structured so that other self.drivers can subclass this
+ test case to ensure compiliance with the DB-API. It is
+ expected that this TestCase may be expanded in the future
+ if ambiguities or edge conditions are discovered.
+
+ The 'Optional Extensions' are not yet being tested.
+
+ self.drivers should subclass this test, overriding setUp, tearDown,
+ self.driver, connect_args and connect_kw_args. Class specification
+ should be as follows:
+
+ import dbapi20
+ class mytest(dbapi20.DatabaseAPI20Test):
+ [...]
+
+ Don't 'import DatabaseAPI20Test from dbapi20', or you will
+ confuse the unit tester - just 'import dbapi20'.
+ '''
+
+ # The self.driver module. This should be the module where the 'connect'
+ # method is to be found
+ driver = None
+ connect_args = () # List of arguments to pass to connect
+ connect_kw_args = {} # Keyword arguments for connect
+ table_prefix = 'dbapi20test_' # If you need to specify a prefix for tables
+
+ ddl1 = 'create table %sbooze (name varchar(20))' % table_prefix
+ ddl2 = 'create table %sbarflys (name varchar(20), drink varchar(30))' % table_prefix
+ xddl1 = 'drop table %sbooze' % table_prefix
+ xddl2 = 'drop table %sbarflys' % table_prefix
+
+ lowerfunc = 'lower' # Name of stored procedure to convert string->lowercase
+
+ # Some drivers may need to override these helpers, for example adding
+ # a 'commit' after the execute.
+ def executeDDL1(self,cursor):
+ cursor.execute(self.ddl1)
+
+ def executeDDL2(self,cursor):
+ cursor.execute(self.ddl2)
+
+ def setUp(self):
+ ''' self.drivers should override this method to perform required setup
+ if any is necessary, such as creating the database.
+ '''
+ pass
+
+ def tearDown(self):
+ ''' self.drivers should override this method to perform required cleanup
+ if any is necessary, such as deleting the test database.
+ The default drops the tables that may be created.
+ '''
+ try:
+ con = self._connect()
+ try:
+ cur = con.cursor()
+ for ddl in (self.xddl1,self.xddl2):
+ try:
+ cur.execute(ddl)
+ con.commit()
+ except self.driver.Error:
+ # Assume table didn't exist. Other tests will check if
+ # execute is busted.
+ pass
+ finally:
+ con.close()
+ except _BaseException:
+ pass
+
+ def _connect(self):
+ try:
+ r = self.driver.connect(
+ *self.connect_args,**self.connect_kw_args
+ )
+ except AttributeError:
+ self.fail("No connect method found in self.driver module")
+ return r
+
+ def test_connect(self):
+ con = self._connect()
+ con.close()
+
+ def test_apilevel(self):
+ try:
+ # Must exist
+ apilevel = self.driver.apilevel
+ # Must equal 2.0
+ self.assertEqual(apilevel,'2.0')
+ except AttributeError:
+ self.fail("Driver doesn't define apilevel")
+
+ def test_threadsafety(self):
+ try:
+ # Must exist
+ threadsafety = self.driver.threadsafety
+ # Must be a valid value
+ _failUnless(self, threadsafety in (0,1,2,3))
+ except AttributeError:
+ self.fail("Driver doesn't define threadsafety")
+
+ def test_paramstyle(self):
+ try:
+ # Must exist
+ paramstyle = self.driver.paramstyle
+ # Must be a valid value
+ _failUnless(self, paramstyle in (
+ 'qmark','numeric','named','format','pyformat'
+ ))
+ except AttributeError:
+ self.fail("Driver doesn't define paramstyle")
+
+ def test_Exceptions(self):
+ # Make sure required exceptions exist, and are in the
+ # defined heirarchy.
+ if sys.version[0] == '3': #under Python 3 StardardError no longer exists
+ self.assertTrue(issubclass(self.driver.Warning,Exception))
+ self.assertTrue(issubclass(self.driver.Error,Exception))
+ else:
+ self.failUnless(issubclass(self.driver.Warning,Exception))
+ self.failUnless(issubclass(self.driver.Error,Exception))
+
+ _failUnless(self,
+ issubclass(self.driver.InterfaceError,self.driver.Error)
+ )
+ _failUnless(self,
+ issubclass(self.driver.DatabaseError,self.driver.Error)
+ )
+ _failUnless(self,
+ issubclass(self.driver.OperationalError,self.driver.Error)
+ )
+ _failUnless(self,
+ issubclass(self.driver.IntegrityError,self.driver.Error)
+ )
+ _failUnless(self,
+ issubclass(self.driver.InternalError,self.driver.Error)
+ )
+ _failUnless(self,
+ issubclass(self.driver.ProgrammingError,self.driver.Error)
+ )
+ _failUnless(self,
+ issubclass(self.driver.NotSupportedError,self.driver.Error)
+ )
+
+ def test_ExceptionsAsConnectionAttributes(self):
+ # OPTIONAL EXTENSION
+ # Test for the optional DB API 2.0 extension, where the exceptions
+ # are exposed as attributes on the Connection object
+ # I figure this optional extension will be implemented by any
+ # driver author who is using this test suite, so it is enabled
+ # by default.
+ con = self._connect()
+ drv = self.driver
+ _failUnless(self,con.Warning is drv.Warning)
+ _failUnless(self,con.Error is drv.Error)
+ _failUnless(self,con.InterfaceError is drv.InterfaceError)
+ _failUnless(self,con.DatabaseError is drv.DatabaseError)
+ _failUnless(self,con.OperationalError is drv.OperationalError)
+ _failUnless(self,con.IntegrityError is drv.IntegrityError)
+ _failUnless(self,con.InternalError is drv.InternalError)
+ _failUnless(self,con.ProgrammingError is drv.ProgrammingError)
+ _failUnless(self,con.NotSupportedError is drv.NotSupportedError)
+
+
+ def test_commit(self):
+ con = self._connect()
+ try:
+ # Commit must work, even if it doesn't do anything
+ con.commit()
+ finally:
+ con.close()
+
+ def test_rollback(self):
+ con = self._connect()
+ # If rollback is defined, it should either work or throw
+ # the documented exception
+ if hasattr(con,'rollback'):
+ try:
+ con.rollback()
+ except self.driver.NotSupportedError:
+ pass
+
+ def test_cursor(self):
+ con = self._connect()
+ try:
+ cur = con.cursor()
+ finally:
+ con.close()
+
+ def test_cursor_isolation(self):
+ con = self._connect()
+ try:
+ # Make sure cursors created from the same connection have
+ # the documented transaction isolation level
+ cur1 = con.cursor()
+ cur2 = con.cursor()
+ self.executeDDL1(cur1)
+ cur1.execute("insert into %sbooze values ('Victoria Bitter')" % (
+ self.table_prefix
+ ))
+ cur2.execute("select name from %sbooze" % self.table_prefix)
+ booze = cur2.fetchall()
+ self.assertEqual(len(booze),1)
+ self.assertEqual(len(booze[0]),1)
+ self.assertEqual(booze[0][0],'Victoria Bitter')
+ finally:
+ con.close()
+
+ def test_description(self):
+ con = self._connect()
+ try:
+ cur = con.cursor()
+ self.executeDDL1(cur)
+ self.assertEqual(cur.description,None,
+ 'cursor.description should be none after executing a '
+ 'statement that can return no rows (such as DDL)'
+ )
+ cur.execute('select name from %sbooze' % self.table_prefix)
+ self.assertEqual(len(cur.description),1,
+ 'cursor.description describes too many columns'
+ )
+ self.assertEqual(len(cur.description[0]),7,
+ 'cursor.description[x] tuples must have 7 elements'
+ )
+ self.assertEqual(cur.description[0][0].lower(),'name',
+ 'cursor.description[x][0] must return column name'
+ )
+ self.assertEqual(cur.description[0][1],self.driver.STRING,
+ 'cursor.description[x][1] must return column type. Got %r'
+ % cur.description[0][1]
+ )
+
+ # Make sure self.description gets reset
+ self.executeDDL2(cur)
+ self.assertEqual(cur.description,None,
+ 'cursor.description not being set to None when executing '
+ 'no-result statements (eg. DDL)'
+ )
+ finally:
+ con.close()
+
+ def test_rowcount(self):
+ con = self._connect()
+ try:
+ cur = con.cursor()
+ self.executeDDL1(cur)
+ _failUnless(self,cur.rowcount in (-1,0), # Bug #543885
+ 'cursor.rowcount should be -1 or 0 after executing no-result '
+ 'statements'
+ )
+ cur.execute("insert into %sbooze values ('Victoria Bitter')" % (
+ self.table_prefix
+ ))
+ _failUnless(self,cur.rowcount in (-1,1),
+ 'cursor.rowcount should == number or rows inserted, or '
+ 'set to -1 after executing an insert statement'
+ )
+ cur.execute("select name from %sbooze" % self.table_prefix)
+ _failUnless(self,cur.rowcount in (-1,1),
+ 'cursor.rowcount should == number of rows returned, or '
+ 'set to -1 after executing a select statement'
+ )
+ self.executeDDL2(cur)
+ self.assertEqual(cur.rowcount,-1,
+ 'cursor.rowcount not being reset to -1 after executing '
+ 'no-result statements'
+ )
+ finally:
+ con.close()
+
+ lower_func = 'lower'
+ def test_callproc(self):
+ con = self._connect()
+ try:
+ cur = con.cursor()
+ if self.lower_func and hasattr(cur,'callproc'):
+ r = cur.callproc(self.lower_func,('FOO',))
+ self.assertEqual(len(r),1)
+ self.assertEqual(r[0],'FOO')
+ r = cur.fetchall()
+ self.assertEqual(len(r),1,'callproc produced no result set')
+ self.assertEqual(len(r[0]),1,
+ 'callproc produced invalid result set'
+ )
+ self.assertEqual(r[0][0],'foo',
+ 'callproc produced invalid results'
+ )
+ finally:
+ con.close()
+
+ def test_close(self):
+ con = self._connect()
+ try:
+ cur = con.cursor()
+ finally:
+ con.close()
+
+ # cursor.execute should raise an Error if called after connection
+ # closed
+ self.assertRaises(self.driver.Error,self.executeDDL1,cur)
+
+ # connection.commit should raise an Error if called after connection'
+ # closed.'
+ self.assertRaises(self.driver.Error,con.commit)
+
+ # connection.close should raise an Error if called more than once
+ #!!! reasonable persons differ about the usefulness of this test and this feature !!!
+ if TEST_FOR_NON_IDEMPOTENT_CLOSE:
+ self.assertRaises(self.driver.Error,con.close)
+
+ def test_execute(self):
+ con = self._connect()
+ try:
+ cur = con.cursor()
+ self._paraminsert(cur)
+ finally:
+ con.close()
+
+ def _paraminsert(self,cur):
+ self.executeDDL2(cur)
+ cur.execute("insert into %sbarflys values ('Victoria Bitter', 'thi%%s :may ca%%(u)se? troub:1e')" % (
+ self.table_prefix
+ ))
+ _failUnless(self,cur.rowcount in (-1,1))
+
+ if self.driver.paramstyle == 'qmark':
+ cur.execute(
+ "insert into %sbarflys values (?, 'thi%%s :may ca%%(u)se? troub:1e')" % self.table_prefix,
+ ("Cooper's",)
+ )
+ elif self.driver.paramstyle == 'numeric':
+ cur.execute(
+ "insert into %sbarflys values (:1, 'thi%%s :may ca%%(u)se? troub:1e')" % self.table_prefix,
+ ("Cooper's",)
+ )
+ elif self.driver.paramstyle == 'named':
+ cur.execute(
+ "insert into %sbarflys values (:beer, 'thi%%s :may ca%%(u)se? troub:1e')" % self.table_prefix,
+ {'beer':"Cooper's"}
+ )
+ elif self.driver.paramstyle == 'format':
+ cur.execute(
+ "insert into %sbarflys values (%%s, 'thi%%s :may ca%%(u)se? troub:1e')" % self.table_prefix,
+ ("Cooper's",)
+ )
+ elif self.driver.paramstyle == 'pyformat':
+ cur.execute(
+ "insert into %sbarflys values (%%(beer)s, 'thi%%s :may ca%%(u)se? troub:1e')" % self.table_prefix,
+ {'beer':"Cooper's"}
+ )
+ else:
+ self.fail('Invalid paramstyle')
+ _failUnless(self,cur.rowcount in (-1,1))
+
+ cur.execute('select name, drink from %sbarflys' % self.table_prefix)
+ res = cur.fetchall()
+ self.assertEqual(len(res),2,'cursor.fetchall returned too few rows')
+ beers = [res[0][0],res[1][0]]
+ beers.sort()
+ self.assertEqual(beers[0],"Cooper's",
+ 'cursor.fetchall retrieved incorrect data, or data inserted '
+ 'incorrectly'
+ )
+ self.assertEqual(beers[1],"Victoria Bitter",
+ 'cursor.fetchall retrieved incorrect data, or data inserted '
+ 'incorrectly'
+ )
+ trouble = "thi%s :may ca%(u)se? troub:1e"
+ self.assertEqual(res[0][1], trouble,
+ 'cursor.fetchall retrieved incorrect data, or data inserted '
+ 'incorrectly. Got=%s, Expected=%s' % (repr(res[0][1]), repr(trouble)))
+ self.assertEqual(res[1][1], trouble,
+ 'cursor.fetchall retrieved incorrect data, or data inserted '
+ 'incorrectly. Got=%s, Expected=%s' % (repr(res[1][1]), repr(trouble)
+ ))
+
+ def test_executemany(self):
+ con = self._connect()
+ try:
+ cur = con.cursor()
+ self.executeDDL1(cur)
+ largs = [ ("Cooper's",) , ("Boag's",) ]
+ margs = [ {'beer': "Cooper's"}, {'beer': "Boag's"} ]
+ if self.driver.paramstyle == 'qmark':
+ cur.executemany(
+ 'insert into %sbooze values (?)' % self.table_prefix,
+ largs
+ )
+ elif self.driver.paramstyle == 'numeric':
+ cur.executemany(
+ 'insert into %sbooze values (:1)' % self.table_prefix,
+ largs
+ )
+ elif self.driver.paramstyle == 'named':
+ cur.executemany(
+ 'insert into %sbooze values (:beer)' % self.table_prefix,
+ margs
+ )
+ elif self.driver.paramstyle == 'format':
+ cur.executemany(
+ 'insert into %sbooze values (%%s)' % self.table_prefix,
+ largs
+ )
+ elif self.driver.paramstyle == 'pyformat':
+ cur.executemany(
+ 'insert into %sbooze values (%%(beer)s)' % (
+ self.table_prefix
+ ),
+ margs
+ )
+ else:
+ self.fail('Unknown paramstyle')
+ _failUnless(self,cur.rowcount in (-1,2),
+ 'insert using cursor.executemany set cursor.rowcount to '
+ 'incorrect value %r' % cur.rowcount
+ )
+ cur.execute('select name from %sbooze' % self.table_prefix)
+ res = cur.fetchall()
+ self.assertEqual(len(res),2,
+ 'cursor.fetchall retrieved incorrect number of rows'
+ )
+ beers = [res[0][0],res[1][0]]
+ beers.sort()
+ self.assertEqual(beers[0],"Boag's",'incorrect data "%s" retrieved' % beers[0])
+ self.assertEqual(beers[1],"Cooper's",'incorrect data retrieved')
+ finally:
+ con.close()
+
+ def test_fetchone(self):
+ con = self._connect()
+ try:
+ cur = con.cursor()
+
+ # cursor.fetchone should raise an Error if called before
+ # executing a select-type query
+ self.assertRaises(self.driver.Error,cur.fetchone)
+
+ # cursor.fetchone should raise an Error if called after
+ # executing a query that cannnot return rows
+ self.executeDDL1(cur)
+ self.assertRaises(self.driver.Error,cur.fetchone)
+
+ cur.execute('select name from %sbooze' % self.table_prefix)
+ self.assertEqual(cur.fetchone(),None,
+ 'cursor.fetchone should return None if a query retrieves '
+ 'no rows'
+ )
+ _failUnless(self,cur.rowcount in (-1,0))
+
+ # cursor.fetchone should raise an Error if called after
+ # executing a query that cannnot return rows
+ cur.execute("insert into %sbooze values ('Victoria Bitter')" % (
+ self.table_prefix
+ ))
+ self.assertRaises(self.driver.Error,cur.fetchone)
+
+ cur.execute('select name from %sbooze' % self.table_prefix)
+ r = cur.fetchone()
+ self.assertEqual(len(r),1,
+ 'cursor.fetchone should have retrieved a single row'
+ )
+ self.assertEqual(r[0],'Victoria Bitter',
+ 'cursor.fetchone retrieved incorrect data'
+ )
+ self.assertEqual(cur.fetchone(),None,
+ 'cursor.fetchone should return None if no more rows available'
+ )
+ _failUnless(self,cur.rowcount in (-1,1))
+ finally:
+ con.close()
+
+ samples = [
+ 'Carlton Cold',
+ 'Carlton Draft',
+ 'Mountain Goat',
+ 'Redback',
+ 'Victoria Bitter',
+ 'XXXX'
+ ]
+
+ def _populate(self):
+ ''' Return a list of sql commands to setup the DB for the fetch
+ tests.
+ '''
+ populate = [
+ "insert into %sbooze values ('%s')" % (self.table_prefix,s)
+ for s in self.samples
+ ]
+ return populate
+
+ def test_fetchmany(self):
+ con = self._connect()
+ try:
+ cur = con.cursor()
+
+ # cursor.fetchmany should raise an Error if called without
+ #issuing a query
+ self.assertRaises(self.driver.Error,cur.fetchmany,4)
+
+ self.executeDDL1(cur)
+ for sql in self._populate():
+ cur.execute(sql)
+
+ cur.execute('select name from %sbooze' % self.table_prefix)
+ r = cur.fetchmany()
+ self.assertEqual(len(r),1,
+ 'cursor.fetchmany retrieved incorrect number of rows, '
+ 'default of arraysize is one.'
+ )
+ cur.arraysize=10
+ r = cur.fetchmany(3) # Should get 3 rows
+ self.assertEqual(len(r),3,
+ 'cursor.fetchmany retrieved incorrect number of rows'
+ )
+ r = cur.fetchmany(4) # Should get 2 more
+ self.assertEqual(len(r),2,
+ 'cursor.fetchmany retrieved incorrect number of rows'
+ )
+ r = cur.fetchmany(4) # Should be an empty sequence
+ self.assertEqual(len(r),0,
+ 'cursor.fetchmany should return an empty sequence after '
+ 'results are exhausted'
+ )
+ _failUnless(self,cur.rowcount in (-1,6))
+
+ # Same as above, using cursor.arraysize
+ cur.arraysize=4
+ cur.execute('select name from %sbooze' % self.table_prefix)
+ r = cur.fetchmany() # Should get 4 rows
+ self.assertEqual(len(r),4,
+ 'cursor.arraysize not being honoured by fetchmany'
+ )
+ r = cur.fetchmany() # Should get 2 more
+ self.assertEqual(len(r),2)
+ r = cur.fetchmany() # Should be an empty sequence
+ self.assertEqual(len(r),0)
+ _failUnless(self,cur.rowcount in (-1,6))
+
+ cur.arraysize=6
+ cur.execute('select name from %sbooze' % self.table_prefix)
+ rows = cur.fetchmany() # Should get all rows
+ _failUnless(self,cur.rowcount in (-1,6))
+ self.assertEqual(len(rows),6)
+ self.assertEqual(len(rows),6)
+ rows = [r[0] for r in rows]
+ rows.sort()
+
+ # Make sure we get the right data back out
+ for i in range(0,6):
+ self.assertEqual(rows[i],self.samples[i],
+ 'incorrect data retrieved by cursor.fetchmany'
+ )
+
+ rows = cur.fetchmany() # Should return an empty list
+ self.assertEqual(len(rows),0,
+ 'cursor.fetchmany should return an empty sequence if '
+ 'called after the whole result set has been fetched'
+ )
+ _failUnless(self,cur.rowcount in (-1,6))
+
+ self.executeDDL2(cur)
+ cur.execute('select name from %sbarflys' % self.table_prefix)
+ r = cur.fetchmany() # Should get empty sequence
+ self.assertEqual(len(r),0,
+ 'cursor.fetchmany should return an empty sequence if '
+ 'query retrieved no rows'
+ )
+ _failUnless(self,cur.rowcount in (-1,0))
+
+ finally:
+ con.close()
+
+ def test_fetchall(self):
+ con = self._connect()
+ try:
+ cur = con.cursor()
+ # cursor.fetchall should raise an Error if called
+ # without executing a query that may return rows (such
+ # as a select)
+ self.assertRaises(self.driver.Error, cur.fetchall)
+
+ self.executeDDL1(cur)
+ for sql in self._populate():
+ cur.execute(sql)
+
+ # cursor.fetchall should raise an Error if called
+ # after executing a a statement that cannot return rows
+ self.assertRaises(self.driver.Error,cur.fetchall)
+
+ cur.execute('select name from %sbooze' % self.table_prefix)
+ rows = cur.fetchall()
+ _failUnless(self,cur.rowcount in (-1,len(self.samples)))
+ self.assertEqual(len(rows),len(self.samples),
+ 'cursor.fetchall did not retrieve all rows'
+ )
+ rows = [r[0] for r in rows]
+ rows.sort()
+ for i in range(0,len(self.samples)):
+ self.assertEqual(rows[i],self.samples[i],
+ 'cursor.fetchall retrieved incorrect rows'
+ )
+ rows = cur.fetchall()
+ self.assertEqual(
+ len(rows),0,
+ 'cursor.fetchall should return an empty list if called '
+ 'after the whole result set has been fetched'
+ )
+ _failUnless(self,cur.rowcount in (-1,len(self.samples)))
+
+ self.executeDDL2(cur)
+ cur.execute('select name from %sbarflys' % self.table_prefix)
+ rows = cur.fetchall()
+ _failUnless(self,cur.rowcount in (-1,0))
+ self.assertEqual(len(rows),0,
+ 'cursor.fetchall should return an empty list if '
+ 'a select query returns no rows'
+ )
+
+ finally:
+ con.close()
+
+ def test_mixedfetch(self):
+ con = self._connect()
+ try:
+ cur = con.cursor()
+ self.executeDDL1(cur)
+ for sql in self._populate():
+ cur.execute(sql)
+
+ cur.execute('select name from %sbooze' % self.table_prefix)
+ rows1 = cur.fetchone()
+ rows23 = cur.fetchmany(2)
+ rows4 = cur.fetchone()
+ rows56 = cur.fetchall()
+ _failUnless(self,cur.rowcount in (-1,6))
+ self.assertEqual(len(rows23),2,
+ 'fetchmany returned incorrect number of rows'
+ )
+ self.assertEqual(len(rows56),2,
+ 'fetchall returned incorrect number of rows'
+ )
+
+ rows = [rows1[0]]
+ rows.extend([rows23[0][0],rows23[1][0]])
+ rows.append(rows4[0])
+ rows.extend([rows56[0][0],rows56[1][0]])
+ rows.sort()
+ for i in range(0,len(self.samples)):
+ self.assertEqual(rows[i],self.samples[i],
+ 'incorrect data retrieved or inserted'
+ )
+ finally:
+ con.close()
+
+ def help_nextset_setUp(self,cur):
+ ''' Should create a procedure called deleteme
+ that returns two result sets, first the
+ number of rows in booze then "name from booze"
+ '''
+ raise NotImplementedError('Helper not implemented')
+ #sql="""
+ # create procedure deleteme as
+ # begin
+ # select count(*) from booze
+ # select name from booze
+ # end
+ #"""
+ #cur.execute(sql)
+
+ def help_nextset_tearDown(self,cur):
+ 'If cleaning up is needed after nextSetTest'
+ raise NotImplementedError('Helper not implemented')
+ #cur.execute("drop procedure deleteme")
+
+ def test_nextset(self):
+ con = self._connect()
+ try:
+ cur = con.cursor()
+ if not hasattr(cur,'nextset'):
+ return
+
+ try:
+ self.executeDDL1(cur)
+ sql=self._populate()
+ for sql in self._populate():
+ cur.execute(sql)
+
+ self.help_nextset_setUp(cur)
+
+ cur.callproc('deleteme')
+ numberofrows=cur.fetchone()
+ assert numberofrows[0]== len(self.samples)
+ assert cur.nextset()
+ names=cur.fetchall()
+ assert len(names) == len(self.samples)
+ s=cur.nextset()
+ assert s == None,'No more return sets, should return None'
+ finally:
+ self.help_nextset_tearDown(cur)
+
+ finally:
+ con.close()
+
+ def test_nextset(self):
+ raise NotImplementedError('Drivers need to override this test')
+
+ def test_arraysize(self):
+ # Not much here - rest of the tests for this are in test_fetchmany
+ con = self._connect()
+ try:
+ cur = con.cursor()
+ _failUnless(self,hasattr(cur,'arraysize'),
+ 'cursor.arraysize must be defined'
+ )
+ finally:
+ con.close()
+
+ def test_setinputsizes(self):
+ con = self._connect()
+ try:
+ cur = con.cursor()
+ cur.setinputsizes( (25,) )
+ self._paraminsert(cur) # Make sure cursor still works
+ finally:
+ con.close()
+
+ def test_setoutputsize_basic(self):
+ # Basic test is to make sure setoutputsize doesn't blow up
+ con = self._connect()
+ try:
+ cur = con.cursor()
+ cur.setoutputsize(1000)
+ cur.setoutputsize(2000,0)
+ self._paraminsert(cur) # Make sure the cursor still works
+ finally:
+ con.close()
+
+ def test_setoutputsize(self):
+ # Real test for setoutputsize is driver dependant
+ raise NotImplementedError('Driver needed to override this test')
+
+ def test_None(self):
+ con = self._connect()
+ try:
+ cur = con.cursor()
+ self.executeDDL1(cur)
+ cur.execute('insert into %sbooze values (NULL)' % self.table_prefix)
+ cur.execute('select name from %sbooze' % self.table_prefix)
+ r = cur.fetchall()
+ self.assertEqual(len(r),1)
+ self.assertEqual(len(r[0]),1)
+ self.assertEqual(r[0][0],None,'NULL value not returned as None')
+ finally:
+ con.close()
+
+ def test_Date(self):
+ d1 = self.driver.Date(2002,12,25)
+ d2 = self.driver.DateFromTicks(time.mktime((2002,12,25,0,0,0,0,0,0)))
+ # Can we assume this? API doesn't specify, but it seems implied
+ # self.assertEqual(str(d1),str(d2))
+
+ def test_Time(self):
+ t1 = self.driver.Time(13,45,30)
+ t2 = self.driver.TimeFromTicks(time.mktime((2001,1,1,13,45,30,0,0,0)))
+ # Can we assume this? API doesn't specify, but it seems implied
+ # self.assertEqual(str(t1),str(t2))
+
+ def test_Timestamp(self):
+ t1 = self.driver.Timestamp(2002,12,25,13,45,30)
+ t2 = self.driver.TimestampFromTicks(
+ time.mktime((2002,12,25,13,45,30,0,0,0))
+ )
+ # Can we assume this? API doesn't specify, but it seems implied
+ # self.assertEqual(str(t1),str(t2))
+
+ def test_Binary(self):
+ b = self.driver.Binary(str2bytes('Something'))
+ b = self.driver.Binary(str2bytes(''))
+
+ def test_STRING(self):
+ _failUnless(self, hasattr(self.driver,'STRING'),
+ 'module.STRING must be defined'
+ )
+
+ def test_BINARY(self):
+ _failUnless(self, hasattr(self.driver,'BINARY'),
+ 'module.BINARY must be defined.'
+ )
+
+ def test_NUMBER(self):
+ _failUnless(self, hasattr(self.driver,'NUMBER'),
+ 'module.NUMBER must be defined.'
+ )
+
+ def test_DATETIME(self):
+ _failUnless(self, hasattr(self.driver,'DATETIME'),
+ 'module.DATETIME must be defined.'
+ )
+
+ def test_ROWID(self):
+ _failUnless(self, hasattr(self.driver,'ROWID'),
+ 'module.ROWID must be defined.'
+ )
diff --git a/Lib/site-packages/adodbapi/test/is64bit.py b/Lib/site-packages/adodbapi/test/is64bit.py
new file mode 100644
index 0000000..bb60360
--- /dev/null
+++ b/Lib/site-packages/adodbapi/test/is64bit.py
@@ -0,0 +1,33 @@
+"""is64bit.Python() --> boolean value of detected Python word size. is64bit.os() --> os build version"""
+import sys
+
+def Python():
+ if sys.platform == 'cli': #IronPython
+ import System
+ return System.IntPtr.Size == 8
+ else:
+ try:
+ return sys.maxsize > 2147483647
+ except AttributeError:
+ return sys.maxint > 2147483647
+
+def os():
+ import platform
+ pm = platform.machine()
+ if pm != '..' and pm.endswith('64'): # recent Python (not Iron)
+ return True
+ else:
+ import os
+ if 'PROCESSOR_ARCHITEW6432' in os.environ:
+ return True # 32 bit program running on 64 bit Windows
+ try:
+ return os.environ['PROCESSOR_ARCHITECTURE'].endswith('64') # 64 bit Windows 64 bit program
+ except IndexError:
+ pass # not Windows
+ try:
+ return '64' in platform.architecture()[0] # this often works in Linux
+ except:
+ return False # is an older version of Python, assume also an older os (best we can guess)
+
+if __name__ == "__main__":
+ print(("is64bit.Python() =", Python(), "is64bit.os() =", os()))
diff --git a/Lib/site-packages/adodbapi/test/setuptestframework.py b/Lib/site-packages/adodbapi/test/setuptestframework.py
new file mode 100644
index 0000000..b5859e2
--- /dev/null
+++ b/Lib/site-packages/adodbapi/test/setuptestframework.py
@@ -0,0 +1,113 @@
+#!/usr/bin/python2
+# Configure this in order to run the testcases.
+"setuptestframework.py v 2.5.0.c9"
+
+import os
+import sys
+import tempfile
+import shutil
+
+try:
+ OSErrors = (WindowsError, OSError)
+except NameError: # not running on Windows
+ OSErrors = OSError
+
+def maketemp():
+ temphome = tempfile.gettempdir()
+ tempdir = os.path.join(temphome, 'adodbapi_test')
+ ## try: ## if running simultanous test, don't erase the other thread's work
+ ## shutil.rmtree(tempdir) # kill off an old copy
+ ## except: pass
+ try: os.mkdir(tempdir)
+ except: pass
+ return tempdir
+
+def _cleanup_function(testfolder, mdb_name):
+ try: os.unlink(os.path.join(testfolder, mdb_name))
+ except: pass # mdb database not present
+ try: shutil.rmtree(os.path.join(testfolder, 'adodbapi'))
+ except: pass # test package not present
+
+def getcleanupfunction():
+ return _cleanup_function
+
+def find_ado_path():
+ adoName = os.path.normpath(os.getcwd() + '/../../adodbapi.py')
+ adoPackage = os.path.dirname(adoName)
+ return adoPackage
+
+# make a new package directory for the test copy of ado
+def makeadopackage(testfolder):
+ adoName = os.path.normpath(os.getcwd() + '/../adodbapi.py')
+ adoPath = os.path.dirname(adoName)
+ if os.path.exists(adoName):
+ newpackage = os.path.join(testfolder,'adodbapi')
+ try:
+ os.mkdir(newpackage)
+ except OSErrors:
+ print('*Note: temporary adodbapi package already exists: may be two versions running?')
+ for f in os.listdir(adoPath):
+ if f.endswith('.py'):
+ shutil.copy(os.path.join(adoPath, f), newpackage)
+ if sys.version_info >= (3,0): # only when running Py3.n
+ save = sys.stdout
+ sys.stdout = None
+ from lib2to3.main import main # use 2to3 to make test package
+ main("lib2to3.fixes",args=['-n','-w', newpackage])
+ sys.stdout = save
+ return testfolder
+ else:
+ raise EnvironmentError('Connot find source of adodbapi to test.')
+
+def makemdb(testfolder, mdb_name):
+ # following setup code borrowed from pywin32 odbc test suite
+ # kindly contributed by Frank Millman.
+ import os
+
+ _accessdatasource = os.path.join(testfolder, mdb_name)
+ if not os.path.isfile(_accessdatasource):
+ try:
+ from win32com.client.gencache import EnsureDispatch
+ from win32com.client import constants
+ win32 = True
+ except ImportError: #perhaps we are running IronPython
+ win32 = False #iron Python
+ try:
+ from System import Activator, Type
+ except:
+ pass
+
+ # Create a brand-new database - what is the story with these?
+ dbe = None
+ for suffix in (".36", ".35", ".30"):
+ try:
+ if win32:
+ dbe = EnsureDispatch("DAO.DBEngine" + suffix)
+ else:
+ type= Type.GetTypeFromProgID("DAO.DBEngine" + suffix)
+ dbe = Activator.CreateInstance(type)
+ break
+ except:
+ pass
+ if dbe:
+ print((' ...Creating ACCESS db at '+_accessdatasource))
+ if win32:
+ workspace = dbe.Workspaces(0)
+ newdb = workspace.CreateDatabase(_accessdatasource,
+ constants.dbLangGeneral,
+ constants.dbEncrypt)
+ else:
+ newdb = dbe.CreateDatabase(_accessdatasource,';LANGID=0x0409;CP=1252;COUNTRY=0')
+ newdb.Close()
+ else:
+ print((' ...copying test ACCESS db to '+_accessdatasource))
+ mdbName = os.path.normpath(os.getcwd() + '/../examples/test.mdb')
+ import shutil
+ shutil.copy(mdbName, _accessdatasource)
+
+ return _accessdatasource
+
+if __name__ == "__main__":
+ print('Setting up a Jet database for server to use for remote testing...')
+ temp = maketemp()
+ makemdb(temp, 'server_test.mdb')
diff --git a/Lib/site-packages/adodbapi/test/test_adodbapi_dbapi20.py b/Lib/site-packages/adodbapi/test/test_adodbapi_dbapi20.py
new file mode 100644
index 0000000..89362c8
--- /dev/null
+++ b/Lib/site-packages/adodbapi/test/test_adodbapi_dbapi20.py
@@ -0,0 +1,181 @@
+print("This module depends on the dbapi20 compliance tests created by Stuart Bishop")
+print("(see db-sig mailing list history for info)")
+import platform
+import unittest
+import sys
+
+import dbapi20
+import setuptestframework
+
+testfolder = setuptestframework.maketemp()
+if '--package' in sys.argv:
+ pth = setuptestframework.makeadopackage(testfolder)
+ sys.argv.remove('--package')
+else:
+ pth = setuptestframework.find_ado_path()
+if pth not in sys.path:
+ sys.path.insert(1,pth)
+# function to clean up the temporary folder -- calling program must run this function before exit.
+cleanup = setuptestframework.getcleanupfunction()
+
+import adodbapi
+import adodbapi.is64bit as is64bit
+db = adodbapi
+
+if '--verbose' in sys.argv:
+ db.adodbapi.verbose = 3
+
+print((adodbapi.version))
+print(("Tested with dbapi20 %s" % dbapi20.__version__))
+
+try:
+ onWindows = bool(sys.getwindowsversion()) # seems to work on all versions of Python
+except:
+ onWindows = False
+
+node = platform.node()
+
+conn_kws = {}
+host = None # if None, will use macro to fill in node name
+instance = r'%s\SQLExpress'
+conn_kws['name'] = 'adotest'
+
+if host is None:
+ conn_kws['macro_getnode'] = ['host', instance]
+else:
+ conn_kws['host'] = host
+
+conn_kws['provider'] = 'Provider=SQLNCLI11;DataTypeCompatibility=80;MARS Connection=True;'
+connStr = "%(provider)s; Integrated Security=SSPI; Initial Catalog=%(name)s;Data Source=%(host)s"
+
+if onWindows and node != "z-PC":
+ pass # default should make a local SQL Server connection
+elif node == "xxx": # try Postgres database
+ _computername = "25.223.161.222"
+ _databasename='adotest'
+ _username = 'adotestuser'
+ _password = '12345678'
+ _driver="PostgreSQL Unicode"
+ _provider = ''
+ connStr = '%sDriver={%s};Server=%s;Database=%s;uid=%s;pwd=%s;' % \
+ (_provider,_driver,_computername,_databasename,_username,_password)
+elif node == "yyy": # ACCESS data base is known to fail some tests.
+ if is64bit.Python():
+ driver = "Microsoft.ACE.OLEDB.12.0"
+ else:
+ driver = "Microsoft.Jet.OLEDB.4.0"
+ testmdb = setuptestframework.makemdb(testfolder)
+ connStr = r"Provider=%s;Data Source=%s" % (driver, testmdb)
+else: # try a remote connection to an SQL server
+ conn_kws['proxy_host'] = '25.44.77.176'
+ import adodbapi.remote
+ db = adodbapi.remote
+
+print(('Using Connection String like=%s' % connStr))
+print(('Keywords=%s' % repr(conn_kws)))
+
+class test_adodbapi(dbapi20.DatabaseAPI20Test):
+ driver = db
+ connect_args = (connStr,)
+ connect_kw_args = conn_kws
+
+ def __init__(self,arg):
+ dbapi20.DatabaseAPI20Test.__init__(self,arg)
+
+ def testMethodName(self):
+ return self.id().split('.')[-1]
+
+ def setUp(self):
+ # Call superclass setUp In case this does something in the
+ # future
+ dbapi20.DatabaseAPI20Test.setUp(self)
+ if self.testMethodName()=='test_callproc':
+ con = self._connect()
+ engine = con.dbms_name
+ ## print('Using database Engine=%s' % engine) ##
+ if engine != 'MS Jet':
+ sql="""
+ create procedure templower
+ @theData varchar(50)
+ as
+ select lower(@theData)
+ """
+ else: # Jet
+ sql="""
+ create procedure templower
+ (theData varchar(50))
+ as
+ select lower(theData);
+ """
+ cur = con.cursor()
+ try:
+ cur.execute(sql)
+ con.commit()
+ except:
+ pass
+ cur.close()
+ con.close()
+ self.lower_func='templower'
+
+
+ def tearDown(self):
+ if self.testMethodName()=='test_callproc':
+ con = self._connect()
+ cur = con.cursor()
+ try:
+ cur.execute("drop procedure templower")
+ except:
+ pass
+ con.commit()
+ dbapi20.DatabaseAPI20Test.tearDown(self)
+
+
+ def help_nextset_setUp(self,cur):
+ 'Should create a procedure called deleteme '
+ 'that returns two result sets, first the number of rows in booze then "name from booze"'
+ sql="""
+ create procedure deleteme as
+ begin
+ select count(*) from %sbooze
+ select name from %sbooze
+ end
+ """ %(self.table_prefix,self.table_prefix)
+ cur.execute(sql)
+
+ def help_nextset_tearDown(self,cur):
+ 'If cleaning up is needed after nextSetTest'
+ try:
+ cur.execute("drop procedure deleteme")
+ except:
+ pass
+
+ def test_nextset(self):
+ con = self._connect()
+ try:
+ cur = con.cursor()
+
+ stmts=[self.ddl1] + self._populate()
+ for sql in stmts:
+ cur.execute(sql)
+
+ self.help_nextset_setUp(cur)
+
+ cur.callproc('deleteme')
+ numberofrows=cur.fetchone()
+ assert numberofrows[0]== 6
+ assert cur.nextset()
+ names=cur.fetchall()
+ assert len(names) == len(self.samples)
+ s=cur.nextset()
+ assert s == None,'No more return sets, should return None'
+ finally:
+ try:
+ self.help_nextset_tearDown(cur)
+ finally:
+ con.close()
+
+ def test_setoutputsize(self): pass
+
+if __name__ == '__main__':
+ unittest.main()
+ cleanup(testfolder, None)
diff --git a/Lib/site-packages/adodbapi/test/tryconnection2.py b/Lib/site-packages/adodbapi/test/tryconnection2.py
new file mode 100644
index 0000000..fa0b8b9
--- /dev/null
+++ b/Lib/site-packages/adodbapi/test/tryconnection2.py
@@ -0,0 +1,46 @@
+# This module may be retired as soon as Python 2.5 support is dropped.
+#
+# It exists only to allow trapping exceptions using the "except [exception list], e" format
+# which is a syntax error in Python 3
+
+def try_connection(verbose, *args, **kwargs):
+ import adodbapi
+
+ if "proxy_host" in kwargs or 'pyro_connection' in kwargs or 'proxy_host' in args:
+ import adodbapi.remote
+ import Pyro4
+ pyroError = Pyro4.errors.PyroError
+ dbconnect = adodbapi.remote.connect
+ remote = True
+ else:
+ dbconnect = adodbapi.connect
+ pyroError = NotImplementedError # (will not occur)
+ remote = False
+ try:
+ s = dbconnect(*args, **kwargs) # connect to server
+ if verbose:
+ print('Connected to:', s.connection_string)
+ print('which has tables:', s.get_table_names())
+ s.close() # thanks, it worked, goodbye
+ except (adodbapi.DatabaseError, pyroError) as inst:
+ print(inst.args[0]) # should be the error message
+ print('***Failed getting connection using=', repr(args), repr(kwargs))
+ if remote:
+ print('** Is your Python2 ado.connection server running?')
+ print('* Have you run "setuptestframework.py" to create server_test.mdb?')
+ return False, (args, kwargs), None
+
+ if remote:
+ print(" (remote)", end=' ')
+ print(" (successful)")
+
+ return True, (args, kwargs, remote), dbconnect
+
+def try_operation_with_expected_exception(expected_exceptions, some_function, args, kwargs):
+ try:
+ some_function(*args, **kwargs)
+ except expected_exceptions as e:
+ return True, e
+ except:
+ raise # an exception other than the expected occurred
+ return False, 'The expected exception did not occur'
diff --git a/Lib/site-packages/adodbapi/test/tryconnection3.py b/Lib/site-packages/adodbapi/test/tryconnection3.py
new file mode 100644
index 0000000..7871174
--- /dev/null
+++ b/Lib/site-packages/adodbapi/test/tryconnection3.py
@@ -0,0 +1,48 @@
+from __future__ import print_function
+# This module may be retired as soon as Python 2.5 support is dropped.
+#
+# It exists only to allow trapping exceptions using the "except [exception list] as e" format
+# which is a syntax error in Python 2.5
+
+def try_connection(verbose, *args, **kwargs):
+ import adodbapi
+
+ if "proxy_host" in kwargs or "pyro_connection" in kwargs or "proxy_host" in args:
+ import adodbapi.remote
+ import Pyro4
+ pyroError = Pyro4.errors.PyroError
+ dbconnect = adodbapi.remote.connect
+ remote = True
+ else:
+ dbconnect = adodbapi.connect
+ pyroError = NotImplementedError # (will not occur)
+ remote = False
+ try:
+ s = dbconnect(*args, **kwargs) # connect to server
+ if verbose:
+ print('Connected to:', s.connection_string)
+ print('which has tables:', s.get_table_names())
+ s.close() # thanks, it worked, goodbye
+ except adodbapi.DatabaseError as inst:
+ print(inst.args[0]) # should be the error message
+ print('***Failed getting connection using=',repr(args),repr(kwargs))
+ if remote:
+ print('** Are you running a *Python3* ado.connection server? **')
+ print('* Have you run "setuptestframework.py" to create server_test.mdb?')
+ return False, (args, kwargs), None
+
+ if remote:
+ print(" (remote)",end="")
+ print(" (successful)")
+
+ return True, (args, kwargs, remote), dbconnect
+
+
+def try_operation_with_expected_exception(expected_exception_list, some_function, *args, **kwargs):
+ try:
+ some_function(*args, **kwargs)
+ except expected_exception_list as e:
+ return True, e
+ except:
+ raise # an exception other than the expected occurred
+ return False, 'The expected exception did not occur'
diff --git a/Lib/site-packages/isapi/PyISAPI_loader.dll b/Lib/site-packages/isapi/PyISAPI_loader.dll
new file mode 100644
index 0000000..d289453
Binary files /dev/null and b/Lib/site-packages/isapi/PyISAPI_loader.dll differ
diff --git a/Lib/site-packages/isapi/README.txt b/Lib/site-packages/isapi/README.txt
new file mode 100644
index 0000000..e65feb9
--- /dev/null
+++ b/Lib/site-packages/isapi/README.txt
@@ -0,0 +1,7 @@
+A Python ISAPI extension. Contributed by Phillip Frantz, and is
+Copyright 2002-2003 by Blackdog Software Pty Ltd.
+
+See the 'samples' directory, and particularly samples\README.txt
+
+You can find documentation in the PyWin32.chm file that comes with pywin32 -
+you can open this from Pythonwin->Help, or from the start menu.
\ No newline at end of file
diff --git a/Lib/site-packages/isapi/__init__.py b/Lib/site-packages/isapi/__init__.py
new file mode 100644
index 0000000..c4778ce
--- /dev/null
+++ b/Lib/site-packages/isapi/__init__.py
@@ -0,0 +1,33 @@
+# The Python ISAPI package.
+
+# Exceptions thrown by the DLL framework.
+class ISAPIError(Exception):
+ def __init__(self, errno, strerror = None, funcname = None):
+ # named attributes match IOError etc.
+ self.errno = errno
+ self.strerror = strerror
+ self.funcname = funcname
+ Exception.__init__(self, errno, strerror, funcname)
+ def __str__(self):
+ if self.strerror is None:
+ try:
+ import win32api
+ self.strerror = win32api.FormatMessage(self.errno).strip()
+ except:
+ self.strerror = "no error message is available"
+ # str() looks like a win32api error.
+ return str( (self.errno, self.strerror, self.funcname) )
+
+class FilterError(ISAPIError):
+ pass
+
+class ExtensionError(ISAPIError):
+ pass
+
+# A little development aid - a filter or extension callback function can
+# raise one of these exceptions, and the handler module will be reloaded.
+# This means you can change your code without restarting IIS.
+# After a reload, your filter/extension will have the GetFilterVersion/
+# GetExtensionVersion function called, but with None as the first arg.
+class InternalReloadException(Exception):
+ pass
diff --git a/Lib/site-packages/isapi/doc/isapi.html b/Lib/site-packages/isapi/doc/isapi.html
new file mode 100644
index 0000000..4662c5c
--- /dev/null
+++ b/Lib/site-packages/isapi/doc/isapi.html
@@ -0,0 +1,92 @@
+
+
+
+Introduction to Python ISAPI support
+
+
Note: if you are viewing this documentation directly from disk,
+most links in this document will fail - you can also find this document in the
+CHM file that comes with pywin32, where the links will work
+
+
Introduction
+This documents Python support for hosting ISAPI exensions and filters inside
+Microsoft Internet Information Server (IIS). It assumes a basic understanding
+of the ISAPI filter and extension mechanism.
+
+In summary, to implement a filter or extension, you provide a Python module
+which defines a Filter and/or Extension class. Once your class has been
+loaded, IIS/ISAPI will, via an extension DLL, call methods on your class.
+
+A filter and a class instance need only provide 3 methods - for filters they
+are called GetFilterVersion, HttpFilterProc and
+TerminateFilter. For extensions they
+are named GetExtensionVersion, HttpExtensionProc and
+TerminateExtension. If you are familiar with writing ISAPI
+extensions in C/C++, these names and their purpose will be familiar.
+
+Most of the work is done in the HttpFilterProc and
+HttpExtensionProc methods. These both take a single
+parameter - an HTTP_FILTER_CONTEXT and
+EXTENSION_CONTROL_BLOCK
+object respectively.
+
+In addition to these components, there is an 'isapi' package, containing
+support facilities (base-classes, exceptions, etc) which can be leveraged
+by the extension.
+
+
Base classes
+There are a number of base classes provided to make writing extensions a little
+simpler. Of particular note is isapi.threaded_extension.ThreadPoolExtension.
+This implements a thread-pool and informs IIS that the request is progressing
+in the background. Your sub-class need only provide a Dispatch
+method, which is called on one of the worker threads rather than the thread
+that the request came in on.
+
+There is base-class for a filter in isapi.simple, but there is no
+equivilent threaded filter - filters work under a different model, where
+background processing is not possible.
+
Samples
+Please see the isapi/samples directory for some sample filters
+and extensions.
+
+
Implementation
+A Python ISAPI filter extension consists of 2 main components:
+
+
A DLL used by ISAPI to interface with Python.
+
A Python script used by that DLL to implement the filter or extension
+functionality
+
+
+
Extension DLL
+The DLL is usually managed automatically by the isapi.install module. As the
+Python script for the extension is installed, a generic DLL provided with
+the isapi package is installed next to the script, and IIS configured to
+use this DLL.
+
+The name of the DLL always has the same base name as the Python script, but
+with a leading underscore (_), and an extension of .dll. For example, the
+sample "redirector.py" will, when installed, have "_redirector.dll" created
+in the same directory.
+
+The Python script may provide 2 entry points - methods named __FilterFactory__
+and __ExtensionFactory__, both taking no arguments and returning a filter or
+extension object.
+
+
Using py2exe and the isapi package
+You can instruct py2exe to create a 'frozen' Python ISAPI filter/extension.
+In this case, py2exe will create a package with everything you need in one
+directory, and the Python source file embedded in the .zip file.
+
+In general, you will want to build a seperate installation executable along
+with the ISAPI extension. This executable will be built from the same script.
+See the ISAPI sample in the py2exe distribution.
diff --git a/Lib/site-packages/isapi/install.py b/Lib/site-packages/isapi/install.py
new file mode 100644
index 0000000..b68d039
--- /dev/null
+++ b/Lib/site-packages/isapi/install.py
@@ -0,0 +1,730 @@
+"""Installation utilities for Python ISAPI filters and extensions."""
+
+# this code adapted from "Tomcat JK2 ISAPI redirector", part of Apache
+# Created July 2004, Mark Hammond.
+import sys, os, imp, shutil, stat
+import operator
+from win32com.client import GetObject, Dispatch
+from win32com.client.gencache import EnsureModule, EnsureDispatch
+import win32api
+import pythoncom
+import winerror
+import traceback
+
+_APP_INPROC = 0
+_APP_OUTPROC = 1
+_APP_POOLED = 2
+_IIS_OBJECT = "IIS://LocalHost/W3SVC"
+_IIS_SERVER = "IIsWebServer"
+_IIS_WEBDIR = "IIsWebDirectory"
+_IIS_WEBVIRTUALDIR = "IIsWebVirtualDir"
+_IIS_FILTERS = "IIsFilters"
+_IIS_FILTER = "IIsFilter"
+
+_DEFAULT_SERVER_NAME = "Default Web Site"
+_DEFAULT_HEADERS = "X-Powered-By: Python"
+_DEFAULT_PROTECTION = _APP_POOLED
+
+# Default is for 'execute' only access - ie, only the extension
+# can be used. This can be overridden via your install script.
+_DEFAULT_ACCESS_EXECUTE = True
+_DEFAULT_ACCESS_READ = False
+_DEFAULT_ACCESS_WRITE = False
+_DEFAULT_ACCESS_SCRIPT = False
+_DEFAULT_CONTENT_INDEXED = False
+_DEFAULT_ENABLE_DIR_BROWSING = False
+_DEFAULT_ENABLE_DEFAULT_DOC = False
+
+_extensions = [ext for ext, _, _ in imp.get_suffixes()]
+is_debug_build = '_d.pyd' in _extensions
+
+this_dir = os.path.abspath(os.path.dirname(__file__))
+
+class FilterParameters:
+ Name = None
+ Description = None
+ Path = None
+ Server = None
+ # Params that control if/how AddExtensionFile is called.
+ AddExtensionFile = True
+ AddExtensionFile_Enabled = True
+ AddExtensionFile_GroupID = None # defaults to Name
+ AddExtensionFile_CanDelete = True
+ AddExtensionFile_Description = None # defaults to Description.
+
+ def __init__(self, **kw):
+ self.__dict__.update(kw)
+
+class VirtualDirParameters:
+ Name = None # Must be provided.
+ Description = None # defaults to Name
+ AppProtection = _DEFAULT_PROTECTION
+ Headers = _DEFAULT_HEADERS
+ Path = None # defaults to WWW root.
+ Type = _IIS_WEBVIRTUALDIR
+ AccessExecute = _DEFAULT_ACCESS_EXECUTE
+ AccessRead = _DEFAULT_ACCESS_READ
+ AccessWrite = _DEFAULT_ACCESS_WRITE
+ AccessScript = _DEFAULT_ACCESS_SCRIPT
+ ContentIndexed = _DEFAULT_CONTENT_INDEXED
+ EnableDirBrowsing = _DEFAULT_ENABLE_DIR_BROWSING
+ EnableDefaultDoc = _DEFAULT_ENABLE_DEFAULT_DOC
+ DefaultDoc = None # Only set in IIS if not None
+ ScriptMaps = []
+ ScriptMapUpdate = "end" # can be 'start', 'end', 'replace'
+ Server = None
+
+ def __init__(self, **kw):
+ self.__dict__.update(kw)
+
+ def is_root(self):
+ "This virtual directory is a root directory if parent and name are blank"
+ parent, name = self.split_path()
+ return not parent and not name
+
+ def split_path(self):
+ return split_path(self.Name)
+
+class ScriptMapParams:
+ Extension = None
+ Module = None
+ Flags = 5
+ Verbs = ""
+ # Params that control if/how AddExtensionFile is called.
+ AddExtensionFile = True
+ AddExtensionFile_Enabled = True
+ AddExtensionFile_GroupID = None # defaults to Name
+ AddExtensionFile_CanDelete = True
+ AddExtensionFile_Description = None # defaults to Description.
+ def __init__(self, **kw):
+ self.__dict__.update(kw)
+
+ def __str__(self):
+ "Format this parameter suitable for IIS"
+ items = [self.Extension, self.Module, self.Flags]
+ # IIS gets upset if there is a trailing verb comma, but no verbs
+ if self.Verbs:
+ items.append(self.Verbs)
+ items = [str(item) for item in items]
+ return ','.join(items)
+
+class ISAPIParameters:
+ ServerName = _DEFAULT_SERVER_NAME
+ # Description = None
+ Filters = []
+ VirtualDirs = []
+ def __init__(self, **kw):
+ self.__dict__.update(kw)
+
+verbose = 1 # The level - 0 is quiet.
+def log(level, what):
+ if verbose >= level:
+ print(what)
+
+# Convert an ADSI COM exception to the Win32 error code embedded in it.
+def _GetWin32ErrorCode(com_exc):
+ hr = com_exc.hresult
+ # If we have more details in the 'excepinfo' struct, use it.
+ if com_exc.excepinfo:
+ hr = com_exc.excepinfo[-1]
+ if winerror.HRESULT_FACILITY(hr) != winerror.FACILITY_WIN32:
+ raise
+ return winerror.SCODE_CODE(hr)
+
+class InstallationError(Exception): pass
+class ItemNotFound(InstallationError): pass
+class ConfigurationError(InstallationError): pass
+
+def FindPath(options, server, name):
+ if name.lower().startswith("iis://"):
+ return name
+ else:
+ if name and name[0] != "/":
+ name = "/"+name
+ return FindWebServer(options, server)+"/ROOT"+name
+
+def LocateWebServerPath(description):
+ """
+ Find an IIS web server whose name or comment matches the provided
+ description (case-insensitive).
+
+ >>> LocateWebServerPath('Default Web Site') # doctest: +SKIP
+
+ or
+
+ >>> LocateWebServerPath('1') #doctest: +SKIP
+ """
+ assert len(description) >= 1, "Server name or comment is required"
+ iis = GetObject(_IIS_OBJECT)
+ description = description.lower().strip()
+ for site in iis:
+ # Name is generally a number, but no need to assume that.
+ site_attributes = [getattr(site, attr, "").lower().strip()
+ for attr in ("Name", "ServerComment")]
+ if description in site_attributes:
+ return site.AdsPath
+ msg = "No web sites match the description '%s'" % description
+ raise ItemNotFound(msg)
+
+def GetWebServer(description = None):
+ """
+ Load the web server instance (COM object) for a given instance
+ or description.
+ If None is specified, the default website is retrieved (indicated
+ by the identifier 1.
+ """
+ description = description or "1"
+ path = LocateWebServerPath(description)
+ server = LoadWebServer(path)
+ return server
+
+def LoadWebServer(path):
+ try:
+ server = GetObject(path)
+ except pythoncom.com_error as details:
+ msg = details.strerror
+ if exc.excepinfo and exc.excepinfo[2]:
+ msg = exc.excepinfo[2]
+ msg = "WebServer %s: %s" % (path, msg)
+ raise ItemNotFound(msg)
+ return server
+
+def FindWebServer(options, server_desc):
+ """
+ Legacy function to allow options to define a .server property
+ to override the other parameter. Use GetWebServer instead.
+ """
+ # options takes precedence
+ server_desc = options.server or server_desc
+ # make sure server_desc is unicode (could be mbcs if passed in
+ # sys.argv).
+ if server_desc and not isinstance(server_desc, str):
+ server_desc = server_desc.decode('mbcs')
+
+ # get the server (if server_desc is None, the default site is acquired)
+ server = GetWebServer(server_desc)
+ return server.adsPath
+
+def split_path(path):
+ """
+ Get the parent path and basename.
+
+ >>> split_path('/')
+ ['', '']
+
+ >>> split_path('')
+ ['', '']
+
+ >>> split_path('foo')
+ ['', 'foo']
+
+ >>> split_path('/foo')
+ ['', 'foo']
+
+ >>> split_path('/foo/bar')
+ ['/foo', 'bar']
+
+ >>> split_path('foo/bar')
+ ['/foo', 'bar']
+ """
+
+ if not path.startswith('/'): path = '/' + path
+ return path.rsplit('/', 1)
+
+def _CreateDirectory(iis_dir, name, params):
+ # We used to go to lengths to keep an existing virtual directory
+ # in place. However, in some cases the existing directories got
+ # into a bad state, and an update failed to get them working.
+ # So we nuke it first. If this is a problem, we could consider adding
+ # a --keep-existing option.
+ try:
+ # Also seen the Class change to a generic IISObject - so nuke
+ # *any* existing object, regardless of Class
+ assert name.strip("/"), "mustn't delete the root!"
+ iis_dir.Delete('', name)
+ log(2, "Deleted old directory '%s'" % (name,))
+ except pythoncom.com_error:
+ pass
+
+ newDir = iis_dir.Create(params.Type, name)
+ log(2, "Creating new directory '%s' in %s..." % (name,iis_dir.Name))
+
+ friendly = params.Description or params.Name
+ newDir.AppFriendlyName = friendly
+
+ # Note that the new directory won't be visible in the IIS UI
+ # unless the directory exists on the filesystem.
+ try:
+ path = params.Path or iis_dir.Path
+ newDir.Path = path
+ except AttributeError:
+ # If params.Type is IIS_WEBDIRECTORY, an exception is thrown
+ pass
+ newDir.AppCreate2(params.AppProtection)
+ # XXX - note that these Headers only work in IIS6 and earlier. IIS7
+ # only supports them on the w3svc node - not even on individial sites,
+ # let alone individual extensions in the site!
+ if params.Headers:
+ newDir.HttpCustomHeaders = params.Headers
+
+ log(2, "Setting directory options...")
+ newDir.AccessExecute = params.AccessExecute
+ newDir.AccessRead = params.AccessRead
+ newDir.AccessWrite = params.AccessWrite
+ newDir.AccessScript = params.AccessScript
+ newDir.ContentIndexed = params.ContentIndexed
+ newDir.EnableDirBrowsing = params.EnableDirBrowsing
+ newDir.EnableDefaultDoc = params.EnableDefaultDoc
+ if params.DefaultDoc is not None:
+ newDir.DefaultDoc = params.DefaultDoc
+ newDir.SetInfo()
+ return newDir
+
+
+def CreateDirectory(params, options):
+ _CallHook(params, "PreInstall", options)
+ if not params.Name:
+ raise ConfigurationError("No Name param")
+ parent, name = params.split_path()
+ target_dir = GetObject(FindPath(options, params.Server, parent))
+
+ if not params.is_root():
+ target_dir = _CreateDirectory(target_dir, name, params)
+
+ AssignScriptMaps(params.ScriptMaps, target_dir, params.ScriptMapUpdate)
+
+ _CallHook(params, "PostInstall", options, target_dir)
+ log(1, "Configured Virtual Directory: %s" % (params.Name,))
+ return target_dir
+
+def AssignScriptMaps(script_maps, target, update='replace'):
+ """Updates IIS with the supplied script map information.
+
+ script_maps is a list of ScriptMapParameter objects
+
+ target is an IIS Virtual Directory to assign the script maps to
+
+ update is a string indicating how to update the maps, one of ('start',
+ 'end', or 'replace')
+ """
+ # determine which function to use to assign script maps
+ script_map_func = '_AssignScriptMaps' + update.capitalize()
+ try:
+ script_map_func = eval(script_map_func)
+ except NameError:
+ msg = "Unknown ScriptMapUpdate option '%s'" % update
+ raise ConfigurationError(msg)
+ # use the str method to format the script maps for IIS
+ script_maps = [str(s) for s in script_maps]
+ # call the correct function
+ script_map_func(target, script_maps)
+ target.SetInfo()
+
+def get_unique_items(sequence, reference):
+ "Return items in sequence that can't be found in reference."
+ return tuple([item for item in sequence if item not in reference])
+
+def _AssignScriptMapsReplace(target, script_maps):
+ target.ScriptMaps = script_maps
+
+def _AssignScriptMapsEnd(target, script_maps):
+ unique_new_maps = get_unique_items(script_maps, target.ScriptMaps)
+ target.ScriptMaps = target.ScriptMaps + unique_new_maps
+
+def _AssignScriptMapsStart(target, script_maps):
+ unique_new_maps = get_unique_items(script_maps, target.ScriptMaps)
+ target.ScriptMaps = unique_new_maps + target.ScriptMaps
+
+def CreateISAPIFilter(filterParams, options):
+ server = FindWebServer(options, filterParams.Server)
+ _CallHook(filterParams, "PreInstall", options)
+ try:
+ filters = GetObject(server+"/Filters")
+ except pythoncom.com_error as exc:
+ # Brand new sites don't have the '/Filters' collection - create it.
+ # Any errors other than 'not found' we shouldn't ignore.
+ if winerror.HRESULT_FACILITY(exc.hresult) != winerror.FACILITY_WIN32 or \
+ winerror.HRESULT_CODE(exc.hresult) != winerror.ERROR_PATH_NOT_FOUND:
+ raise
+ server_ob = GetObject(server)
+ filters = server_ob.Create(_IIS_FILTERS, "Filters")
+ filters.FilterLoadOrder = ""
+ filters.SetInfo()
+
+ # As for VirtualDir, delete an existing one.
+ assert filterParams.Name.strip("/"), "mustn't delete the root!"
+ try:
+ filters.Delete(_IIS_FILTER, filterParams.Name)
+ log(2, "Deleted old filter '%s'" % (filterParams.Name,))
+ except pythoncom.com_error:
+ pass
+ newFilter = filters.Create(_IIS_FILTER, filterParams.Name)
+ log(2, "Created new ISAPI filter...")
+ assert os.path.isfile(filterParams.Path)
+ newFilter.FilterPath = filterParams.Path
+ newFilter.FilterDescription = filterParams.Description
+ newFilter.SetInfo()
+ load_order = [b.strip() for b in filters.FilterLoadOrder.split(",") if b]
+ if filterParams.Name not in load_order:
+ load_order.append(filterParams.Name)
+ filters.FilterLoadOrder = ",".join(load_order)
+ filters.SetInfo()
+ _CallHook(filterParams, "PostInstall", options, newFilter)
+ log (1, "Configured Filter: %s" % (filterParams.Name,))
+ return newFilter
+
+def DeleteISAPIFilter(filterParams, options):
+ _CallHook(filterParams, "PreRemove", options)
+ server = FindWebServer(options, filterParams.Server)
+ ob_path = server+"/Filters"
+ try:
+ filters = GetObject(ob_path)
+ except pythoncom.com_error as details:
+ # failure to open the filters just means a totally clean IIS install
+ # (IIS5 at least has no 'Filters' key when freshly installed).
+ log(2, "ISAPI filter path '%s' did not exist." % (ob_path,))
+ return
+ try:
+ assert filterParams.Name.strip("/"), "mustn't delete the root!"
+ filters.Delete(_IIS_FILTER, filterParams.Name)
+ log(2, "Deleted ISAPI filter '%s'" % (filterParams.Name,))
+ except pythoncom.com_error as details:
+ rc = _GetWin32ErrorCode(details)
+ if rc != winerror.ERROR_PATH_NOT_FOUND:
+ raise
+ log(2, "ISAPI filter '%s' did not exist." % (filterParams.Name,))
+ # Remove from the load order
+ load_order = [b.strip() for b in filters.FilterLoadOrder.split(",") if b]
+ if filterParams.Name in load_order:
+ load_order.remove(filterParams.Name)
+ filters.FilterLoadOrder = ",".join(load_order)
+ filters.SetInfo()
+ _CallHook(filterParams, "PostRemove", options)
+ log (1, "Deleted Filter: %s" % (filterParams.Name,))
+
+def _AddExtensionFile(module, def_groupid, def_desc, params, options):
+ group_id = params.AddExtensionFile_GroupID or def_groupid
+ desc = params.AddExtensionFile_Description or def_desc
+ try:
+ ob = GetObject(_IIS_OBJECT)
+ ob.AddExtensionFile(module,
+ params.AddExtensionFile_Enabled,
+ group_id,
+ params.AddExtensionFile_CanDelete,
+ desc)
+ log(2, "Added extension file '%s' (%s)" % (module, desc))
+ except (pythoncom.com_error, AttributeError) as details:
+ # IIS5 always fails. Probably should upgrade this to
+ # complain more loudly if IIS6 fails.
+ log(2, "Failed to add extension file '%s': %s" % (module, details))
+
+def AddExtensionFiles(params, options):
+ """Register the modules used by the filters/extensions as a trusted
+ 'extension module' - required by the default IIS6 security settings."""
+ # Add each module only once.
+ added = {}
+ for vd in params.VirtualDirs:
+ for smp in vd.ScriptMaps:
+ if smp.Module not in added and smp.AddExtensionFile:
+ _AddExtensionFile(smp.Module, vd.Name, vd.Description, smp,
+ options)
+ added[smp.Module] = True
+
+ for fd in params.Filters:
+ if fd.Path not in added and fd.AddExtensionFile:
+ _AddExtensionFile(fd.Path, fd.Name, fd.Description, fd, options)
+ added[fd.Path] = True
+
+def _DeleteExtensionFileRecord(module, options):
+ try:
+ ob = GetObject(_IIS_OBJECT)
+ ob.DeleteExtensionFileRecord(module)
+ log(2, "Deleted extension file record for '%s'" % module)
+ except (pythoncom.com_error, AttributeError) as details:
+ log(2, "Failed to remove extension file '%s': %s" % (module, details))
+
+def DeleteExtensionFileRecords(params, options):
+ deleted = {} # only remove each .dll once.
+ for vd in params.VirtualDirs:
+ for smp in vd.ScriptMaps:
+ if smp.Module not in deleted and smp.AddExtensionFile:
+ _DeleteExtensionFileRecord(smp.Module, options)
+ deleted[smp.Module] = True
+
+ for filter_def in params.Filters:
+ if filter_def.Path not in deleted and filter_def.AddExtensionFile:
+ _DeleteExtensionFileRecord(filter_def.Path, options)
+ deleted[filter_def.Path] = True
+
+def CheckLoaderModule(dll_name):
+ suffix = ""
+ if is_debug_build: suffix = "_d"
+ template = os.path.join(this_dir,
+ "PyISAPI_loader" + suffix + ".dll")
+ if not os.path.isfile(template):
+ raise ConfigurationError(
+ "Template loader '%s' does not exist" % (template,))
+ # We can't do a simple "is newer" check, as the DLL is specific to the
+ # Python version. So we check the date-time and size are identical,
+ # and skip the copy in that case.
+ src_stat = os.stat(template)
+ try:
+ dest_stat = os.stat(dll_name)
+ except os.error:
+ same = 0
+ else:
+ same = src_stat[stat.ST_SIZE]==dest_stat[stat.ST_SIZE] and \
+ src_stat[stat.ST_MTIME]==dest_stat[stat.ST_MTIME]
+ if not same:
+ log(2, "Updating %s->%s" % (template, dll_name))
+ shutil.copyfile(template, dll_name)
+ shutil.copystat(template, dll_name)
+ else:
+ log(2, "%s is up to date." % (dll_name,))
+
+def _CallHook(ob, hook_name, options, *extra_args):
+ func = getattr(ob, hook_name, None)
+ if func is not None:
+ args = (ob,options) + extra_args
+ func(*args)
+
+def Install(params, options):
+ _CallHook(params, "PreInstall", options)
+ for vd in params.VirtualDirs:
+ CreateDirectory(vd, options)
+
+ for filter_def in params.Filters:
+ CreateISAPIFilter(filter_def, options)
+
+ AddExtensionFiles(params, options)
+
+ _CallHook(params, "PostInstall", options)
+
+def RemoveDirectory(params, options):
+ if params.is_root():
+ return
+ try:
+ directory = GetObject(FindPath(options, params.Server, params.Name))
+ except pythoncom.com_error as details:
+ rc = _GetWin32ErrorCode(details)
+ if rc != winerror.ERROR_PATH_NOT_FOUND:
+ raise
+ log(2, "VirtualDirectory '%s' did not exist" % params.Name)
+ directory = None
+ if directory is not None:
+ # Be robust should IIS get upset about unloading.
+ try:
+ directory.AppUnLoad()
+ except:
+ exc_val = sys.exc_info()[1]
+ log(2, "AppUnLoad() for %s failed: %s" % (params.Name, exc_val))
+ # Continue trying to delete it.
+ try:
+ parent = GetObject(directory.Parent)
+ parent.Delete(directory.Class, directory.Name)
+ log (1, "Deleted Virtual Directory: %s" % (params.Name,))
+ except:
+ exc_val = sys.exc_info()[1]
+ log(1, "Failed to remove directory %s: %s" % (params.Name, exc_val))
+
+def RemoveScriptMaps(vd_params, options):
+ "Remove script maps from the already installed virtual directory"
+ parent, name = vd_params.split_path()
+ target_dir = GetObject(FindPath(options, vd_params.Server, parent))
+ installed_maps = list(target_dir.ScriptMaps)
+ for _map in map(str, vd_params.ScriptMaps):
+ if _map in installed_maps:
+ installed_maps.remove(_map)
+ target_dir.ScriptMaps = installed_maps
+ target_dir.SetInfo()
+
+def Uninstall(params, options):
+ _CallHook(params, "PreRemove", options)
+
+ DeleteExtensionFileRecords(params, options)
+
+ for vd in params.VirtualDirs:
+ _CallHook(vd, "PreRemove", options)
+
+ RemoveDirectory(vd, options)
+ if vd.is_root():
+ # if this is installed to the root virtual directory, we can't delete it
+ # so remove the script maps.
+ RemoveScriptMaps(vd, options)
+
+ _CallHook(vd, "PostRemove", options)
+
+ for filter_def in params.Filters:
+ DeleteISAPIFilter(filter_def, options)
+ _CallHook(params, "PostRemove", options)
+
+# Patch up any missing module names in the params, replacing them with
+# the DLL name that hosts this extension/filter.
+def _PatchParamsModule(params, dll_name, file_must_exist = True):
+ if file_must_exist:
+ if not os.path.isfile(dll_name):
+ raise ConfigurationError("%s does not exist" % (dll_name,))
+
+ # Patch up all references to the DLL.
+ for f in params.Filters:
+ if f.Path is None: f.Path = dll_name
+ for d in params.VirtualDirs:
+ for sm in d.ScriptMaps:
+ if sm.Module is None: sm.Module = dll_name
+
+def GetLoaderModuleName(mod_name, check_module = None):
+ # find the name of the DLL hosting us.
+ # By default, this is "_{module_base_name}.dll"
+ if hasattr(sys, "frozen"):
+ # What to do? The .dll knows its name, but this is likely to be
+ # executed via a .exe, which does not know.
+ base, ext = os.path.splitext(mod_name)
+ path, base = os.path.split(base)
+ # handle the common case of 'foo.exe'/'foow.exe'
+ if base.endswith('w'):
+ base = base[:-1]
+ # For py2exe, we have '_foo.dll' as the standard pyisapi loader - but
+ # 'foo.dll' is what we use (it just delegates).
+ # So no leading '_' on the installed name.
+ dll_name = os.path.abspath(os.path.join(path, base + ".dll"))
+ else:
+ base, ext = os.path.splitext(mod_name)
+ path, base = os.path.split(base)
+ dll_name = os.path.abspath(os.path.join(path, "_" + base + ".dll"))
+ # Check we actually have it.
+ if check_module is None: check_module = not hasattr(sys, "frozen")
+ if check_module:
+ CheckLoaderModule(dll_name)
+ return dll_name
+
+# Note the 'log' params to these 'builtin' args - old versions of pywin32
+# didn't log at all in this function (by intent; anyone calling this was
+# responsible). So existing code that calls this function with the old
+# signature (ie, without a 'log' param) still gets the same behaviour as
+# before...
+
+def InstallModule(conf_module_name, params, options, log=lambda *args:None):
+ "Install the extension"
+ if not hasattr(sys, "frozen"):
+ conf_module_name = os.path.abspath(conf_module_name)
+ if not os.path.isfile(conf_module_name):
+ raise ConfigurationError("%s does not exist" % (conf_module_name,))
+
+ loader_dll = GetLoaderModuleName(conf_module_name)
+ _PatchParamsModule(params, loader_dll)
+ Install(params, options)
+ log(1, "Installation complete.")
+
+def UninstallModule(conf_module_name, params, options, log=lambda *args:None):
+ "Remove the extension"
+ loader_dll = GetLoaderModuleName(conf_module_name, False)
+ _PatchParamsModule(params, loader_dll, False)
+ Uninstall(params, options)
+ log(1, "Uninstallation complete.")
+
+standard_arguments = {
+ "install" : InstallModule,
+ "remove" : UninstallModule,
+}
+
+def build_usage(handler_map):
+ docstrings = [handler.__doc__ for handler in handler_map.values()]
+ all_args = dict(zip(iter(handler_map.keys()), docstrings))
+ arg_names = "|".join(iter(all_args.keys()))
+ usage_string = "%prog [options] [" + arg_names + "]\n"
+ usage_string += "commands:\n"
+ for arg, desc in all_args.items():
+ usage_string += " %-10s: %s" % (arg, desc) + "\n"
+ return usage_string[:-1]
+
+def MergeStandardOptions(options, params):
+ """
+ Take an options object generated by the command line and merge
+ the values into the IISParameters object.
+ """
+ pass
+
+
+# We support 2 ways of extending our command-line/install support.
+# * Many of the installation items allow you to specify "PreInstall",
+# "PostInstall", "PreRemove" and "PostRemove" hooks
+# All hooks are called with the 'params' object being operated on, and
+# the 'optparser' options for this session (ie, the command-line options)
+# PostInstall for VirtualDirectories and Filters both have an additional
+# param - the ADSI object just created.
+# * You can pass your own option parser for us to use, and/or define a map
+# with your own custom arg handlers. It is a map of 'arg'->function.
+# The function is called with (options, log_fn, arg). The function's
+# docstring is used in the usage output.
+def HandleCommandLine(params, argv=None, conf_module_name = None,
+ default_arg = "install",
+ opt_parser = None, custom_arg_handlers = {}):
+ """Perform installation or removal of an ISAPI filter or extension.
+
+ This module handles standard command-line options and configuration
+ information, and installs, removes or updates the configuration of an
+ ISAPI filter or extension.
+
+ You must pass your configuration information in params - all other
+ arguments are optional, and allow you to configure the installation
+ process.
+ """
+ global verbose
+ from optparse import OptionParser
+
+ argv = argv or sys.argv
+ if not conf_module_name:
+ conf_module_name = sys.argv[0]
+ # convert to a long name so that if we were somehow registered with
+ # the "short" version but unregistered with the "long" version we
+ # still work (that will depend on exactly how the installer was
+ # started)
+ try:
+ conf_module_name = win32api.GetLongPathName(conf_module_name)
+ except win32api.error as exc:
+ log(2, "Couldn't determine the long name for %r: %s" %
+ (conf_module_name, exc))
+
+ if opt_parser is None:
+ # Build our own parser.
+ parser = OptionParser(usage='')
+ else:
+ # The caller is providing their own filter, presumably with their
+ # own options all setup.
+ parser = opt_parser
+
+ # build a usage string if we don't have one.
+ if not parser.get_usage():
+ all_handlers = standard_arguments.copy()
+ all_handlers.update(custom_arg_handlers)
+ parser.set_usage(build_usage(all_handlers))
+
+ # allow the user to use uninstall as a synonym for remove if it wasn't
+ # defined by the custom arg handlers.
+ all_handlers.setdefault('uninstall', all_handlers['remove'])
+
+ parser.add_option("-q", "--quiet",
+ action="store_false", dest="verbose", default=True,
+ help="don't print status messages to stdout")
+ parser.add_option("-v", "--verbosity", action="count",
+ dest="verbose", default=1,
+ help="increase the verbosity of status messages")
+ parser.add_option("", "--server", action="store",
+ help="Specifies the IIS server to install/uninstall on." \
+ " Default is '%s/1'" % (_IIS_OBJECT,))
+
+ (options, args) = parser.parse_args(argv[1:])
+ MergeStandardOptions(options, params)
+ verbose = options.verbose
+ if not args:
+ args = [default_arg]
+ try:
+ for arg in args:
+ handler = all_handlers[arg]
+ handler(conf_module_name, params, options, log)
+ except (ItemNotFound, InstallationError) as details:
+ if options.verbose > 1:
+ traceback.print_exc()
+ print("%s: %s" % (details.__class__.__name__, details))
+ except KeyError:
+ parser.error("Invalid arg '%s'" % arg)
diff --git a/Lib/site-packages/isapi/isapicon.py b/Lib/site-packages/isapi/isapicon.py
new file mode 100644
index 0000000..79dd479
--- /dev/null
+++ b/Lib/site-packages/isapi/isapicon.py
@@ -0,0 +1,120 @@
+"""Constants needed by ISAPI filters and extensions."""
+# ======================================================================
+# Copyright 2002-2003 by Blackdog Software Pty Ltd.
+#
+# All Rights Reserved
+#
+# Permission to use, copy, modify, and distribute this software and
+# its documentation for any purpose and without fee is hereby
+# granted, provided that the above copyright notice appear in all
+# copies and that both that copyright notice and this permission
+# notice appear in supporting documentation, and that the name of
+# Blackdog Software not be used in advertising or publicity pertaining to
+# distribution of the software without specific, written prior
+# permission.
+#
+# BLACKDOG SOFTWARE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+# NO EVENT SHALL BLACKDOG SOFTWARE BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+# ======================================================================
+
+# HTTP reply codes
+
+HTTP_CONTINUE = 100
+HTTP_SWITCHING_PROTOCOLS = 101
+HTTP_PROCESSING = 102
+HTTP_OK = 200
+HTTP_CREATED = 201
+HTTP_ACCEPTED = 202
+HTTP_NON_AUTHORITATIVE = 203
+HTTP_NO_CONTENT = 204
+HTTP_RESET_CONTENT = 205
+HTTP_PARTIAL_CONTENT = 206
+HTTP_MULTI_STATUS = 207
+HTTP_MULTIPLE_CHOICES = 300
+HTTP_MOVED_PERMANENTLY = 301
+HTTP_MOVED_TEMPORARILY = 302
+HTTP_SEE_OTHER = 303
+HTTP_NOT_MODIFIED = 304
+HTTP_USE_PROXY = 305
+HTTP_TEMPORARY_REDIRECT = 307
+HTTP_BAD_REQUEST = 400
+HTTP_UNAUTHORIZED = 401
+HTTP_PAYMENT_REQUIRED = 402
+HTTP_FORBIDDEN = 403
+HTTP_NOT_FOUND = 404
+HTTP_METHOD_NOT_ALLOWED = 405
+HTTP_NOT_ACCEPTABLE = 406
+HTTP_PROXY_AUTHENTICATION_REQUIRED= 407
+HTTP_REQUEST_TIME_OUT = 408
+HTTP_CONFLICT = 409
+HTTP_GONE = 410
+HTTP_LENGTH_REQUIRED = 411
+HTTP_PRECONDITION_FAILED = 412
+HTTP_REQUEST_ENTITY_TOO_LARGE = 413
+HTTP_REQUEST_URI_TOO_LARGE = 414
+HTTP_UNSUPPORTED_MEDIA_TYPE = 415
+HTTP_RANGE_NOT_SATISFIABLE = 416
+HTTP_EXPECTATION_FAILED = 417
+HTTP_UNPROCESSABLE_ENTITY = 422
+HTTP_INTERNAL_SERVER_ERROR = 500
+HTTP_NOT_IMPLEMENTED = 501
+HTTP_BAD_GATEWAY = 502
+HTTP_SERVICE_UNAVAILABLE = 503
+HTTP_GATEWAY_TIME_OUT = 504
+HTTP_VERSION_NOT_SUPPORTED = 505
+HTTP_VARIANT_ALSO_VARIES = 506
+
+HSE_STATUS_SUCCESS = 1
+HSE_STATUS_SUCCESS_AND_KEEP_CONN = 2
+HSE_STATUS_PENDING = 3
+HSE_STATUS_ERROR = 4
+
+SF_NOTIFY_SECURE_PORT = 0x00000001
+SF_NOTIFY_NONSECURE_PORT = 0x00000002
+SF_NOTIFY_READ_RAW_DATA = 0x00008000
+SF_NOTIFY_PREPROC_HEADERS = 0x00004000
+SF_NOTIFY_AUTHENTICATION = 0x00002000
+SF_NOTIFY_URL_MAP = 0x00001000
+SF_NOTIFY_ACCESS_DENIED = 0x00000800
+SF_NOTIFY_SEND_RESPONSE = 0x00000040
+SF_NOTIFY_SEND_RAW_DATA = 0x00000400
+SF_NOTIFY_LOG = 0x00000200
+SF_NOTIFY_END_OF_REQUEST = 0x00000080
+SF_NOTIFY_END_OF_NET_SESSION = 0x00000100
+
+SF_NOTIFY_ORDER_HIGH = 0x00080000
+SF_NOTIFY_ORDER_MEDIUM = 0x00040000
+SF_NOTIFY_ORDER_LOW = 0x00020000
+SF_NOTIFY_ORDER_DEFAULT = SF_NOTIFY_ORDER_LOW
+
+SF_NOTIFY_ORDER_MASK = (SF_NOTIFY_ORDER_HIGH | \
+ SF_NOTIFY_ORDER_MEDIUM | \
+ SF_NOTIFY_ORDER_LOW)
+
+SF_STATUS_REQ_FINISHED = 134217728 # 0x8000000
+SF_STATUS_REQ_FINISHED_KEEP_CONN = 134217728 + 1
+SF_STATUS_REQ_NEXT_NOTIFICATION = 134217728 + 2
+SF_STATUS_REQ_HANDLED_NOTIFICATION = 134217728 + 3
+SF_STATUS_REQ_ERROR = 134217728 + 4
+SF_STATUS_REQ_READ_NEXT = 134217728 + 5
+
+HSE_IO_SYNC = 0x00000001 # for WriteClient
+HSE_IO_ASYNC = 0x00000002 # for WriteClient/TF/EU
+HSE_IO_DISCONNECT_AFTER_SEND = 0x00000004 # for TF
+HSE_IO_SEND_HEADERS = 0x00000008 # for TF
+HSE_IO_NODELAY = 0x00001000 # turn off nagling
+# These two are only used by VectorSend
+HSE_IO_FINAL_SEND = 0x00000010
+HSE_IO_CACHE_RESPONSE = 0x00000020
+
+HSE_EXEC_URL_NO_HEADERS = 0x02
+HSE_EXEC_URL_IGNORE_CURRENT_INTERCEPTOR = 0x04
+HSE_EXEC_URL_IGNORE_VALIDATION_AND_RANGE = 0x10
+HSE_EXEC_URL_DISABLE_CUSTOM_ERROR = 0x20
+HSE_EXEC_URL_SSI_CMD = 0x40
+HSE_EXEC_URL_HTTP_CACHE_ELIGIBLE = 0x80
diff --git a/Lib/site-packages/isapi/samples/README.txt b/Lib/site-packages/isapi/samples/README.txt
new file mode 100644
index 0000000..5024b10
--- /dev/null
+++ b/Lib/site-packages/isapi/samples/README.txt
@@ -0,0 +1,20 @@
+In this directory you will find examples of ISAPI filters and extensions.
+
+The filter loading mechanism works like this:
+* IIS loads the special Python "loader" DLL. This DLL will generally have a
+ leading underscore as part of its name.
+* This loader DLL looks for a Python module, by removing the first letter of
+ the DLL base name.
+
+This means that an ISAPI extension module consists of 2 key files - the loader
+DLL (eg, "_MyIISModule.dll", and a Python module (which for this example
+would be "MyIISModule.py")
+
+When you install an ISAPI extension, the installation code checks to see if
+there is a loader DLL for your implementation file - if one does not exist,
+or the standard loader is different, it is copied and renamed accordingly.
+
+We use this mechanism to provide the maximum separation between different
+Python extensions installed on the same server - otherwise filter order and
+other tricky IIS semantics would need to be replicated. Also, each filter
+gets its own thread-pool, etc.
diff --git a/Lib/site-packages/isapi/samples/advanced.py b/Lib/site-packages/isapi/samples/advanced.py
new file mode 100644
index 0000000..f08586d
--- /dev/null
+++ b/Lib/site-packages/isapi/samples/advanced.py
@@ -0,0 +1,196 @@
+# This extension demonstrates some advanced features of the Python ISAPI
+# framework.
+# We demonstrate:
+# * Reloading your Python module without shutting down IIS (eg, when your
+# .py implementation file changes.)
+# * Custom command-line handling - both additional options and commands.
+# * Using a query string - any part of the URL after a '?' is assumed to
+# be "variable names" separated by '&' - we will print the values of
+# these server variables.
+# * If the tail portion of the URL is "ReportUnhealthy", IIS will be
+# notified we are unhealthy via a HSE_REQ_REPORT_UNHEALTHY request.
+# Whether this is acted upon depends on if the IIS health-checking
+# tools are installed, but you should always see the reason written
+# to the Windows event log - see the IIS documentation for more.
+
+from isapi import isapicon
+from isapi.simple import SimpleExtension
+import sys, os, stat
+
+if hasattr(sys, "isapidllhandle"):
+ import win32traceutil
+
+# Notes on reloading
+# If your HttpFilterProc or HttpExtensionProc functions raises
+# 'isapi.InternalReloadException', the framework will not treat it
+# as an error but instead will terminate your extension, reload your
+# extension module, re-initialize the instance, and re-issue the request.
+# The Initialize functions are called with None as their param. The
+# return code from the terminate function is ignored.
+#
+# This is all the framework does to help you. It is up to your code
+# when you raise this exception. This sample uses a Win32 "find
+# notification". Whenever windows tells us one of the files in the
+# directory has changed, we check if the time of our source-file has
+# changed, and set a flag. Next imcoming request, we check the flag and
+# raise the special exception if set.
+#
+# The end result is that the module is automatically reloaded whenever
+# the source-file changes - you need take no further action to see your
+# changes reflected in the running server.
+
+# The framework only reloads your module - if you have libraries you
+# depend on and also want reloaded, you must arrange for this yourself.
+# One way of doing this would be to special case the import of these
+# modules. Eg:
+# --
+# try:
+# my_module = reload(my_module) # module already imported - reload it
+# except NameError:
+# import my_module # first time around - import it.
+# --
+# When your module is imported for the first time, the NameError will
+# be raised, and the module imported. When the ISAPI framework reloads
+# your module, the existing module will avoid the NameError, and allow
+# you to reload that module.
+
+from isapi import InternalReloadException
+import win32event, win32file, winerror, win32con, threading
+
+try:
+ reload_counter += 1
+except NameError:
+ reload_counter = 0
+
+# A watcher thread that checks for __file__ changing.
+# When it detects it, it simply sets "change_detected" to true.
+class ReloadWatcherThread(threading.Thread):
+ def __init__(self):
+ self.change_detected = False
+ self.filename = __file__
+ if self.filename.endswith("c") or self.filename.endswith("o"):
+ self.filename = self.filename[:-1]
+ self.handle = win32file.FindFirstChangeNotification(
+ os.path.dirname(self.filename),
+ False, # watch tree?
+ win32con.FILE_NOTIFY_CHANGE_LAST_WRITE)
+ threading.Thread.__init__(self)
+
+ def run(self):
+ last_time = os.stat(self.filename)[stat.ST_MTIME]
+ while 1:
+ try:
+ rc = win32event.WaitForSingleObject(self.handle,
+ win32event.INFINITE)
+ win32file.FindNextChangeNotification(self.handle)
+ except win32event.error as details:
+ # handle closed - thread should terminate.
+ if details.winerror != winerror.ERROR_INVALID_HANDLE:
+ raise
+ break
+ this_time = os.stat(self.filename)[stat.ST_MTIME]
+ if this_time != last_time:
+ print("Detected file change - flagging for reload.")
+ self.change_detected = True
+ last_time = this_time
+
+ def stop(self):
+ win32file.FindCloseChangeNotification(self.handle)
+
+# The ISAPI extension - handles requests in our virtual dir, and sends the
+# response to the client.
+class Extension(SimpleExtension):
+ "Python advanced sample Extension"
+ def __init__(self):
+ self.reload_watcher = ReloadWatcherThread()
+ self.reload_watcher.start()
+
+ def HttpExtensionProc(self, ecb):
+ # NOTE: If you use a ThreadPoolExtension, you must still perform
+ # this check in HttpExtensionProc - raising the exception from
+ # The "Dispatch" method will just cause the exception to be
+ # rendered to the browser.
+ if self.reload_watcher.change_detected:
+ print("Doing reload")
+ raise InternalReloadException
+
+ url = ecb.GetServerVariable("UNICODE_URL")
+ if url.endswith("ReportUnhealthy"):
+ ecb.ReportUnhealthy("I'm a little sick")
+
+ ecb.SendResponseHeaders("200 OK", "Content-Type: text/html\r\n\r\n", 0)
+ print("
", file=ecb)
+ for q in queries:
+ val = ecb.GetServerVariable(q, '<no such variable>')
+ print("%s=%r" % (q, val), file=ecb)
+ print("
", file=ecb)
+
+ print("This module has been imported", file=ecb)
+ print("%d times" % (reload_counter,), file=ecb)
+ print("", file=ecb)
+ ecb.close()
+ return isapicon.HSE_STATUS_SUCCESS
+
+ def TerminateExtension(self, status):
+ self.reload_watcher.stop()
+
+# The entry points for the ISAPI extension.
+def __ExtensionFactory__():
+ return Extension()
+
+# Our special command line customization.
+# Pre-install hook for our virtual directory.
+def PreInstallDirectory(params, options):
+ # If the user used our special '--description' option,
+ # then we override our default.
+ if options.description:
+ params.Description = options.description
+
+# Post install hook for our entire script
+def PostInstall(params, options):
+ print()
+ print("The sample has been installed.")
+ print("Point your browser to /AdvancedPythonSample")
+ print("If you modify the source file and reload the page,")
+ print("you should see the reload counter increment")
+
+# Handler for our custom 'status' argument.
+def status_handler(options, log, arg):
+ "Query the status of something"
+ print("Everything seems to be fine!")
+
+custom_arg_handlers = {"status": status_handler}
+
+if __name__=='__main__':
+ # If run from the command-line, install ourselves.
+ from isapi.install import *
+ params = ISAPIParameters(PostInstall = PostInstall)
+ # Setup the virtual directories - this is a list of directories our
+ # extension uses - in this case only 1.
+ # Each extension has a "script map" - this is the mapping of ISAPI
+ # extensions.
+ sm = [
+ ScriptMapParams(Extension="*", Flags=0)
+ ]
+ vd = VirtualDirParameters(Name="AdvancedPythonSample",
+ Description = Extension.__doc__,
+ ScriptMaps = sm,
+ ScriptMapUpdate = "replace",
+ # specify the pre-install hook.
+ PreInstall = PreInstallDirectory
+ )
+ params.VirtualDirs = [vd]
+ # Setup our custom option parser.
+ from optparse import OptionParser
+ parser = OptionParser('') # blank usage, so isapi sets it.
+ parser.add_option("", "--description",
+ action="store",
+ help="custom description to use for the virtual directory")
+
+ HandleCommandLine(params, opt_parser=parser,
+ custom_arg_handlers = custom_arg_handlers)
diff --git a/Lib/site-packages/isapi/samples/redirector.py b/Lib/site-packages/isapi/samples/redirector.py
new file mode 100644
index 0000000..99c5d88
--- /dev/null
+++ b/Lib/site-packages/isapi/samples/redirector.py
@@ -0,0 +1,109 @@
+# This is a sample ISAPI extension written in Python.
+#
+# Please see README.txt in this directory, and specifically the
+# information about the "loader" DLL - installing this sample will create
+# "_redirector.dll" in the current directory. The readme explains this.
+
+# Executing this script (or any server config script) will install the extension
+# into your web server. As the server executes, the PyISAPI framework will load
+# this module and create your Extension and Filter objects.
+
+# This is the simplest possible redirector (or proxy) we can write. The
+# extension installs with a mask of '*' in the root of the site.
+# As an added bonus though, we optionally show how, on IIS6 and later, we
+# can use HSE_ERQ_EXEC_URL to ignore certain requests - in IIS5 and earlier
+# we can only do this with an ISAPI filter - see redirector_with_filter for
+# an example. If this sample is run on IIS5 or earlier it simply ignores
+# any excludes.
+
+from isapi import isapicon, threaded_extension
+import sys
+import traceback
+try:
+ from urllib.request import urlopen
+except ImportError:
+ # py3k spelling...
+ from urllib.request import urlopen
+import win32api
+
+# sys.isapidllhandle will exist when we are loaded by the IIS framework.
+# In this case we redirect our output to the win32traceutil collector.
+if hasattr(sys, "isapidllhandle"):
+ import win32traceutil
+
+# The site we are proxying.
+proxy = "http://www.python.org"
+
+# Urls we exclude (ie, allow IIS to handle itself) - all are lowered,
+# and these entries exist by default on Vista...
+excludes = ["/iisstart.htm", "/welcome.png"]
+
+# An "io completion" function, called when ecb.ExecURL completes...
+def io_callback(ecb, url, cbIO, errcode):
+ # Get the status of our ExecURL
+ httpstatus, substatus, win32 = ecb.GetExecURLStatus()
+ print("ExecURL of %r finished with http status %d.%d, win32 status %d (%s)" % (
+ url, httpstatus, substatus, win32, win32api.FormatMessage(win32).strip()))
+ # nothing more to do!
+ ecb.DoneWithSession()
+
+# The ISAPI extension - handles all requests in the site.
+class Extension(threaded_extension.ThreadPoolExtension):
+ "Python sample Extension"
+ def Dispatch(self, ecb):
+ # Note that our ThreadPoolExtension base class will catch exceptions
+ # in our Dispatch method, and write the traceback to the client.
+ # That is perfect for this sample, so we don't catch our own.
+ #print 'IIS dispatching "%s"' % (ecb.GetServerVariable("URL"),)
+ url = ecb.GetServerVariable("URL").decode("ascii")
+ for exclude in excludes:
+ if url.lower().startswith(exclude):
+ print("excluding %s" % url)
+ if ecb.Version < 0x60000:
+ print("(but this is IIS5 or earlier - can't do 'excludes')")
+ else:
+ ecb.IOCompletion(io_callback, url)
+ ecb.ExecURL(None, None, None, None, None, isapicon.HSE_EXEC_URL_IGNORE_CURRENT_INTERCEPTOR)
+ return isapicon.HSE_STATUS_PENDING
+
+ new_url = proxy + url
+ print("Opening %s" % new_url)
+ fp = urlopen(new_url)
+ headers = fp.info()
+ # subtle py3k breakage: in py3k, str(headers) has normalized \r\n
+ # back to \n and also stuck an extra \n term. py2k leaves the
+ # \r\n from the server in tact and finishes with a single term.
+ if sys.version_info < (3,0):
+ header_text = str(headers) + "\r\n"
+ else:
+ # take *all* trailing \n off, replace remaining with
+ # \r\n, then add the 2 trailing \r\n.
+ header_text = str(headers).rstrip('\n').replace('\n', '\r\n') + '\r\n\r\n'
+ ecb.SendResponseHeaders("200 OK", header_text, False)
+ ecb.WriteClient(fp.read())
+ ecb.DoneWithSession()
+ print("Returned data from '%s'" % (new_url,))
+ return isapicon.HSE_STATUS_SUCCESS
+
+# The entry points for the ISAPI extension.
+def __ExtensionFactory__():
+ return Extension()
+
+if __name__=='__main__':
+ # If run from the command-line, install ourselves.
+ from isapi.install import *
+ params = ISAPIParameters()
+ # Setup the virtual directories - this is a list of directories our
+ # extension uses - in this case only 1.
+ # Each extension has a "script map" - this is the mapping of ISAPI
+ # extensions.
+ sm = [
+ ScriptMapParams(Extension="*", Flags=0)
+ ]
+ vd = VirtualDirParameters(Name="/",
+ Description = Extension.__doc__,
+ ScriptMaps = sm,
+ ScriptMapUpdate = "replace"
+ )
+ params.VirtualDirs = [vd]
+ HandleCommandLine(params)
diff --git a/Lib/site-packages/isapi/samples/redirector_asynch.py b/Lib/site-packages/isapi/samples/redirector_asynch.py
new file mode 100644
index 0000000..bdd9908
--- /dev/null
+++ b/Lib/site-packages/isapi/samples/redirector_asynch.py
@@ -0,0 +1,78 @@
+# This is a sample ISAPI extension written in Python.
+
+# This is like the other 'redirector' samples, but uses asnch IO when writing
+# back to the client (it does *not* use asynch io talking to the remote
+# server!)
+
+from isapi import isapicon, threaded_extension
+import sys
+import traceback
+import urllib.request, urllib.parse, urllib.error
+
+# sys.isapidllhandle will exist when we are loaded by the IIS framework.
+# In this case we redirect our output to the win32traceutil collector.
+if hasattr(sys, "isapidllhandle"):
+ import win32traceutil
+
+# The site we are proxying.
+proxy = "http://www.python.org"
+
+# We synchronously read chunks of this size then asynchronously write them.
+CHUNK_SIZE=8192
+
+# The callback made when IIS completes the asynch write.
+def io_callback(ecb, fp, cbIO, errcode):
+ print("IO callback", ecb, fp, cbIO, errcode)
+ chunk = fp.read(CHUNK_SIZE)
+ if chunk:
+ ecb.WriteClient(chunk, isapicon.HSE_IO_ASYNC)
+ # and wait for the next callback to say this chunk is done.
+ else:
+ # eof - say we are complete.
+ fp.close()
+ ecb.DoneWithSession()
+
+# The ISAPI extension - handles all requests in the site.
+class Extension(threaded_extension.ThreadPoolExtension):
+ "Python sample proxy server - asynch version."
+ def Dispatch(self, ecb):
+ print('IIS dispatching "%s"' % (ecb.GetServerVariable("URL"),))
+ url = ecb.GetServerVariable("URL")
+
+ new_url = proxy + url
+ print("Opening %s" % new_url)
+ fp = urllib.request.urlopen(new_url)
+ headers = fp.info()
+ ecb.SendResponseHeaders("200 OK", str(headers) + "\r\n", False)
+ # now send the first chunk asynchronously
+ ecb.ReqIOCompletion(io_callback, fp)
+ chunk = fp.read(CHUNK_SIZE)
+ if chunk:
+ ecb.WriteClient(chunk, isapicon.HSE_IO_ASYNC)
+ return isapicon.HSE_STATUS_PENDING
+ # no data - just close things now.
+ ecb.DoneWithSession()
+ return isapicon.HSE_STATUS_SUCCESS
+
+# The entry points for the ISAPI extension.
+def __ExtensionFactory__():
+ return Extension()
+
+if __name__=='__main__':
+ # If run from the command-line, install ourselves.
+ from isapi.install import *
+ params = ISAPIParameters()
+ # Setup the virtual directories - this is a list of directories our
+ # extension uses - in this case only 1.
+ # Each extension has a "script map" - this is the mapping of ISAPI
+ # extensions.
+ sm = [
+ ScriptMapParams(Extension="*", Flags=0)
+ ]
+ vd = VirtualDirParameters(Name="/",
+ Description = Extension.__doc__,
+ ScriptMaps = sm,
+ ScriptMapUpdate = "replace"
+ )
+ params.VirtualDirs = [vd]
+ HandleCommandLine(params)
diff --git a/Lib/site-packages/isapi/samples/redirector_with_filter.py b/Lib/site-packages/isapi/samples/redirector_with_filter.py
new file mode 100644
index 0000000..3b2afaf
--- /dev/null
+++ b/Lib/site-packages/isapi/samples/redirector_with_filter.py
@@ -0,0 +1,155 @@
+# This is a sample configuration file for an ISAPI filter and extension
+# written in Python.
+#
+# Please see README.txt in this directory, and specifically the
+# information about the "loader" DLL - installing this sample will create
+# "_redirector_with_filter.dll" in the current directory. The readme explains
+# this.
+
+# Executing this script (or any server config script) will install the extension
+# into your web server. As the server executes, the PyISAPI framework will load
+# this module and create your Extension and Filter objects.
+
+# This sample provides sample redirector:
+# It is implemented by a filter and an extension, so that some requests can
+# be ignored. Compare with 'redirector_simple' which avoids the filter, but
+# is unable to selectively ignore certain requests.
+# The process is sample uses is:
+# * The filter is installed globally, as all filters are.
+# * A Virtual Directory named "python" is setup. This dir has our ISAPI
+# extension as the only application, mapped to file-extension '*'. Thus, our
+# extension handles *all* requests in this directory.
+# The basic process is that the filter does URL rewriting, redirecting every
+# URL to our Virtual Directory. Our extension then handles this request,
+# forwarding the data from the proxied site.
+# For example:
+# * URL of "index.html" comes in.
+# * Filter rewrites this to "/python/index.html"
+# * Our extension sees the full "/python/index.html", removes the leading
+# portion, and opens and forwards the remote URL.
+
+
+# This sample is very small - it avoid most error handling, etc. It is for
+# demonstration purposes only.
+
+from isapi import isapicon, threaded_extension
+from isapi.simple import SimpleFilter
+import sys
+import traceback
+import urllib.request, urllib.parse, urllib.error
+
+# sys.isapidllhandle will exist when we are loaded by the IIS framework.
+# In this case we redirect our output to the win32traceutil collector.
+if hasattr(sys, "isapidllhandle"):
+ import win32traceutil
+
+# The site we are proxying.
+proxy = "http://www.python.org"
+# The name of the virtual directory we install in, and redirect from.
+virtualdir = "/python"
+
+# The key feature of this redirector over the simple redirector is that it
+# can choose to ignore certain responses by having the filter not rewrite them
+# to our virtual dir. For this sample, we just exclude the IIS help directory.
+
+# The ISAPI extension - handles requests in our virtual dir, and sends the
+# response to the client.
+class Extension(threaded_extension.ThreadPoolExtension):
+ "Python sample Extension"
+ def Dispatch(self, ecb):
+ # Note that our ThreadPoolExtension base class will catch exceptions
+ # in our Dispatch method, and write the traceback to the client.
+ # That is perfect for this sample, so we don't catch our own.
+ #print 'IIS dispatching "%s"' % (ecb.GetServerVariable("URL"),)
+ url = ecb.GetServerVariable("URL")
+ if url.startswith(virtualdir):
+ new_url = proxy + url[len(virtualdir):]
+ print("Opening", new_url)
+ fp = urllib.request.urlopen(new_url)
+ headers = fp.info()
+ ecb.SendResponseHeaders("200 OK", str(headers) + "\r\n", False)
+ ecb.WriteClient(fp.read())
+ ecb.DoneWithSession()
+ print("Returned data from '%s'!" % (new_url,))
+ else:
+ # this should never happen - we should only see requests that
+ # start with our virtual directory name.
+ print("Not proxying '%s'" % (url,))
+
+
+# The ISAPI filter.
+class Filter(SimpleFilter):
+ "Sample Python Redirector"
+ filter_flags = isapicon.SF_NOTIFY_PREPROC_HEADERS | \
+ isapicon.SF_NOTIFY_ORDER_DEFAULT
+
+ def HttpFilterProc(self, fc):
+ #print "Filter Dispatch"
+ nt = fc.NotificationType
+ if nt != isapicon.SF_NOTIFY_PREPROC_HEADERS:
+ return isapicon.SF_STATUS_REQ_NEXT_NOTIFICATION
+
+ pp = fc.GetData()
+ url = pp.GetHeader("url")
+ #print "URL is '%s'" % (url,)
+ prefix = virtualdir
+ if not url.startswith(prefix):
+ new_url = prefix + url
+ print("New proxied URL is '%s'" % (new_url,))
+ pp.SetHeader("url", new_url)
+ # For the sake of demonstration, show how the FilterContext
+ # attribute is used. It always starts out life as None, and
+ # any assignments made are automatically decref'd by the
+ # framework during a SF_NOTIFY_END_OF_NET_SESSION notification.
+ if fc.FilterContext is None:
+ fc.FilterContext = 0
+ fc.FilterContext += 1
+ print("This is request number %d on this connection" % fc.FilterContext)
+ return isapicon.SF_STATUS_REQ_HANDLED_NOTIFICATION
+ else:
+ print("Filter ignoring URL '%s'" % (url,))
+
+ # Some older code that handled SF_NOTIFY_URL_MAP.
+ #~ print "Have URL_MAP notify"
+ #~ urlmap = fc.GetData()
+ #~ print "URI is", urlmap.URL
+ #~ print "Path is", urlmap.PhysicalPath
+ #~ if urlmap.URL.startswith("/UC/"):
+ #~ # Find the /UC/ in the physical path, and nuke it (except
+ #~ # as the path is physical, it is \)
+ #~ p = urlmap.PhysicalPath
+ #~ pos = p.index("\\UC\\")
+ #~ p = p[:pos] + p[pos+3:]
+ #~ p = r"E:\src\pyisapi\webroot\PyTest\formTest.htm"
+ #~ print "New path is", p
+ #~ urlmap.PhysicalPath = p
+
+# The entry points for the ISAPI extension.
+def __FilterFactory__():
+ return Filter()
+def __ExtensionFactory__():
+ return Extension()
+
+if __name__=='__main__':
+ # If run from the command-line, install ourselves.
+ from isapi.install import *
+ params = ISAPIParameters()
+ # Setup all filters - these are global to the site.
+ params.Filters = [
+ FilterParameters(Name="PythonRedirector",
+ Description=Filter.__doc__),
+ ]
+ # Setup the virtual directories - this is a list of directories our
+ # extension uses - in this case only 1.
+ # Each extension has a "script map" - this is the mapping of ISAPI
+ # extensions.
+ sm = [
+ ScriptMapParams(Extension="*", Flags=0)
+ ]
+ vd = VirtualDirParameters(Name=virtualdir[1:],
+ Description = Extension.__doc__,
+ ScriptMaps = sm,
+ ScriptMapUpdate = "replace"
+ )
+ params.VirtualDirs = [vd]
+ HandleCommandLine(params)
diff --git a/Lib/site-packages/isapi/samples/test.py b/Lib/site-packages/isapi/samples/test.py
new file mode 100644
index 0000000..5b595cb
--- /dev/null
+++ b/Lib/site-packages/isapi/samples/test.py
@@ -0,0 +1,154 @@
+# This extension is used mainly for testing purposes - it is not
+# designed to be a simple sample, but instead is a hotch-potch of things
+# that attempts to exercise the framework.
+
+from isapi import isapicon
+from isapi.simple import SimpleExtension
+import sys, os, stat
+
+if hasattr(sys, "isapidllhandle"):
+ import win32traceutil
+
+# We use the same reload support as 'advanced.py' demonstrates.
+from isapi import InternalReloadException
+import win32event, win32file, winerror, win32con, threading
+
+# A watcher thread that checks for __file__ changing.
+# When it detects it, it simply sets "change_detected" to true.
+class ReloadWatcherThread(threading.Thread):
+ def __init__(self):
+ self.change_detected = False
+ self.filename = __file__
+ if self.filename.endswith("c") or self.filename.endswith("o"):
+ self.filename = self.filename[:-1]
+ self.handle = win32file.FindFirstChangeNotification(
+ os.path.dirname(self.filename),
+ False, # watch tree?
+ win32con.FILE_NOTIFY_CHANGE_LAST_WRITE)
+ threading.Thread.__init__(self)
+
+ def run(self):
+ last_time = os.stat(self.filename)[stat.ST_MTIME]
+ while 1:
+ try:
+ rc = win32event.WaitForSingleObject(self.handle,
+ win32event.INFINITE)
+ win32file.FindNextChangeNotification(self.handle)
+ except win32event.error as details:
+ # handle closed - thread should terminate.
+ if details.winerror != winerror.ERROR_INVALID_HANDLE:
+ raise
+ break
+ this_time = os.stat(self.filename)[stat.ST_MTIME]
+ if this_time != last_time:
+ print("Detected file change - flagging for reload.")
+ self.change_detected = True
+ last_time = this_time
+
+ def stop(self):
+ win32file.FindCloseChangeNotification(self.handle)
+
+def TransmitFileCallback(ecb, hFile, cbIO, errCode):
+ print("Transmit complete!")
+ ecb.close()
+
+# The ISAPI extension - handles requests in our virtual dir, and sends the
+# response to the client.
+class Extension(SimpleExtension):
+ "Python test Extension"
+ def __init__(self):
+ self.reload_watcher = ReloadWatcherThread()
+ self.reload_watcher.start()
+
+ def HttpExtensionProc(self, ecb):
+ # NOTE: If you use a ThreadPoolExtension, you must still perform
+ # this check in HttpExtensionProc - raising the exception from
+ # The "Dispatch" method will just cause the exception to be
+ # rendered to the browser.
+ if self.reload_watcher.change_detected:
+ print("Doing reload")
+ raise InternalReloadException
+
+ if ecb.GetServerVariable("UNICODE_URL").endswith("test.py"):
+ file_flags = win32con.FILE_FLAG_SEQUENTIAL_SCAN | win32con.FILE_FLAG_OVERLAPPED
+ hfile = win32file.CreateFile(__file__, win32con.GENERIC_READ,
+ 0, None, win32con.OPEN_EXISTING,
+ file_flags, None)
+ flags = isapicon.HSE_IO_ASYNC | isapicon.HSE_IO_DISCONNECT_AFTER_SEND | \
+ isapicon.HSE_IO_SEND_HEADERS
+ # We pass hFile to the callback simply as a way of keeping it alive
+ # for the duration of the transmission
+ try:
+ ecb.TransmitFile(TransmitFileCallback, hfile,
+ int(hfile),
+ "200 OK",
+ 0, 0, None, None, flags)
+ except:
+ # Errors keep this source file open!
+ hfile.Close()
+ raise
+ else:
+ # default response
+ ecb.SendResponseHeaders("200 OK", "Content-Type: text/html\r\n\r\n", 0)
+ print("", file=ecb)
+ print("The root of this site is at", ecb.MapURLToPath("/"), file=ecb)
+ print("", file=ecb)
+ ecb.close()
+ return isapicon.HSE_STATUS_SUCCESS
+
+ def TerminateExtension(self, status):
+ self.reload_watcher.stop()
+
+# The entry points for the ISAPI extension.
+def __ExtensionFactory__():
+ return Extension()
+
+# Our special command line customization.
+# Pre-install hook for our virtual directory.
+def PreInstallDirectory(params, options):
+ # If the user used our special '--description' option,
+ # then we override our default.
+ if options.description:
+ params.Description = options.description
+
+# Post install hook for our entire script
+def PostInstall(params, options):
+ print()
+ print("The sample has been installed.")
+ print("Point your browser to /PyISAPITest")
+
+# Handler for our custom 'status' argument.
+def status_handler(options, log, arg):
+ "Query the status of something"
+ print("Everything seems to be fine!")
+
+custom_arg_handlers = {"status": status_handler}
+
+if __name__=='__main__':
+ # If run from the command-line, install ourselves.
+ from isapi.install import *
+ params = ISAPIParameters(PostInstall = PostInstall)
+ # Setup the virtual directories - this is a list of directories our
+ # extension uses - in this case only 1.
+ # Each extension has a "script map" - this is the mapping of ISAPI
+ # extensions.
+ sm = [
+ ScriptMapParams(Extension="*", Flags=0)
+ ]
+ vd = VirtualDirParameters(Name="PyISAPITest",
+ Description = Extension.__doc__,
+ ScriptMaps = sm,
+ ScriptMapUpdate = "replace",
+ # specify the pre-install hook.
+ PreInstall = PreInstallDirectory
+ )
+ params.VirtualDirs = [vd]
+ # Setup our custom option parser.
+ from optparse import OptionParser
+ parser = OptionParser('') # blank usage, so isapi sets it.
+ parser.add_option("", "--description",
+ action="store",
+ help="custom description to use for the virtual directory")
+
+ HandleCommandLine(params, opt_parser=parser,
+ custom_arg_handlers = custom_arg_handlers)
diff --git a/Lib/site-packages/isapi/simple.py b/Lib/site-packages/isapi/simple.py
new file mode 100644
index 0000000..3c653c7
--- /dev/null
+++ b/Lib/site-packages/isapi/simple.py
@@ -0,0 +1,68 @@
+"""Simple base-classes for extensions and filters.
+
+None of the filter and extension functions are considered 'optional' by the
+framework. These base-classes provide simple implementations for the
+Initialize and Terminate functions, allowing you to omit them,
+
+It is not necessary to use these base-classes - but if you don't, you
+must ensure each of the required methods are implemented.
+"""
+
+class SimpleExtension:
+ "Base class for a simple ISAPI extension"
+ def __init__(self):
+ pass
+
+ def GetExtensionVersion(self, vi):
+ """Called by the ISAPI framework to get the extension version
+
+ The default implementation uses the classes docstring to
+ set the extension description."""
+ # nod to our reload capability - vi is None when we are reloaded.
+ if vi is not None:
+ vi.ExtensionDesc = self.__doc__
+
+ def HttpExtensionProc(self, control_block):
+ """Called by the ISAPI framework for each extension request.
+
+ sub-classes must provide an implementation for this method.
+ """
+ raise NotImplementedError("sub-classes should override HttpExtensionProc")
+
+ def TerminateExtension(self, status):
+ """Called by the ISAPI framework as the extension terminates.
+ """
+ pass
+
+class SimpleFilter:
+ "Base class for a a simple ISAPI filter"
+ filter_flags = None
+ def __init__(self):
+ pass
+
+ def GetFilterVersion(self, fv):
+ """Called by the ISAPI framework to get the extension version
+
+ The default implementation uses the classes docstring to
+ set the extension description, and uses the classes
+ filter_flags attribute to set the ISAPI filter flags - you
+ must specify filter_flags in your class.
+ """
+ if self.filter_flags is None:
+ raise RuntimeError("You must specify the filter flags")
+ # nod to our reload capability - fv is None when we are reloaded.
+ if fv is not None:
+ fv.Flags = self.filter_flags
+ fv.FilterDesc = self.__doc__
+
+ def HttpFilterProc(self, fc):
+ """Called by the ISAPI framework for each filter request.
+
+ sub-classes must provide an implementation for this method.
+ """
+ raise NotImplementedError("sub-classes should override HttpExtensionProc")
+
+ def TerminateFilter(self, status):
+ """Called by the ISAPI framework as the filter terminates.
+ """
+ pass
diff --git a/Lib/site-packages/isapi/test/README.txt b/Lib/site-packages/isapi/test/README.txt
new file mode 100644
index 0000000..b4356a7
--- /dev/null
+++ b/Lib/site-packages/isapi/test/README.txt
@@ -0,0 +1,3 @@
+This is a directory for tests of the PyISAPI framework.
+
+For demos, please see the pyisapi 'samples' directory.
\ No newline at end of file
diff --git a/Lib/site-packages/isapi/test/extension_simple.py b/Lib/site-packages/isapi/test/extension_simple.py
new file mode 100644
index 0000000..991e491
--- /dev/null
+++ b/Lib/site-packages/isapi/test/extension_simple.py
@@ -0,0 +1,111 @@
+# This is an ISAPI extension purely for testing purposes. It is NOT
+# a 'demo' (even though it may be useful!)
+#
+# Install this extension, then point your browser to:
+# "http://localhost/pyisapi_test/test1"
+# This will execute the method 'test1' below. See below for the list of
+# test methods that are acceptable.
+
+from isapi import isapicon, threaded_extension, ExtensionError
+from isapi.simple import SimpleFilter
+import traceback
+import urllib.request, urllib.parse, urllib.error
+import winerror
+
+# If we have no console (eg, am running from inside IIS), redirect output
+# somewhere useful - in this case, the standard win32 trace collector.
+import win32api
+try:
+ win32api.GetConsoleTitle()
+except win32api.error:
+ # No console - redirect
+ import win32traceutil
+
+# The ISAPI extension - handles requests in our virtual dir, and sends the
+# response to the client.
+class Extension(threaded_extension.ThreadPoolExtension):
+ "Python ISAPI Tester"
+ def Dispatch(self, ecb):
+ print('Tester dispatching "%s"' % (ecb.GetServerVariable("URL"),))
+ url = ecb.GetServerVariable("URL")
+ test_name = url.split("/")[-1]
+ meth = getattr(self, test_name, None)
+ if meth is None:
+ raise AttributeError("No test named '%s'" % (test_name,))
+ result = meth(ecb)
+ if result is None:
+ # This means the test finalized everything
+ return
+ ecb.SendResponseHeaders("200 OK", "Content-type: text/html\r\n\r\n",
+ False)
+ print("Finished running test ", test_name, "", file=ecb)
+ print("
", file=ecb)
+ print(result, file=ecb)
+ print("
", file=ecb)
+ print("", file=ecb)
+ ecb.DoneWithSession()
+
+ def test1(self, ecb):
+ try:
+ ecb.GetServerVariable("foo bar")
+ raise RuntimeError("should have failed!")
+ except ExtensionError as err:
+ assert err.errno == winerror.ERROR_INVALID_INDEX, err
+ return "worked!"
+
+ def test_long_vars(self, ecb):
+ qs = ecb.GetServerVariable("QUERY_STRING")
+ # Our implementation has a default buffer size of 8k - so we test
+ # the code that handles an overflow by ensuring there are more
+ # than 8k worth of chars in the URL.
+ expected_query = ('x' * 8500)
+ if len(qs)==0:
+ # Just the URL with no query part - redirect to myself, but with
+ # a huge query portion.
+ me = ecb.GetServerVariable("URL")
+ headers = "Location: " + me + "?" + expected_query + "\r\n\r\n"
+ ecb.SendResponseHeaders("301 Moved", headers)
+ ecb.DoneWithSession()
+ return None
+ if qs == expected_query:
+ return "Total length of variable is %d - test worked!" % (len(qs),)
+ else:
+ return "Unexpected query portion! Got %d chars, expected %d" % \
+ (len(qs), len(expected_query))
+
+ def test_unicode_vars(self, ecb):
+ # We need to check that we are running IIS6! This seems the only
+ # effective way from an extension.
+ ver = float(ecb.GetServerVariable("SERVER_SOFTWARE").split('/')[1])
+ if ver < 6.0:
+ return "This is IIS version %g - unicode only works in IIS6 and later" % ver
+
+ us = ecb.GetServerVariable("UNICODE_SERVER_NAME")
+ if not isinstance(us, str):
+ raise RuntimeError("unexpected type!")
+ if us != str(ecb.GetServerVariable("SERVER_NAME")):
+ raise RuntimeError("Unicode and non-unicode values were not the same")
+ return "worked!"
+
+# The entry points for the ISAPI extension.
+def __ExtensionFactory__():
+ return Extension()
+
+if __name__=='__main__':
+ # If run from the command-line, install ourselves.
+ from isapi.install import *
+ params = ISAPIParameters()
+ # Setup the virtual directories - this is a list of directories our
+ # extension uses - in this case only 1.
+ # Each extension has a "script map" - this is the mapping of ISAPI
+ # extensions.
+ sm = [
+ ScriptMapParams(Extension="*", Flags=0)
+ ]
+ vd = VirtualDirParameters(Name="pyisapi_test",
+ Description = Extension.__doc__,
+ ScriptMaps = sm,
+ ScriptMapUpdate = "replace"
+ )
+ params.VirtualDirs = [vd]
+ HandleCommandLine(params)
diff --git a/Lib/site-packages/isapi/threaded_extension.py b/Lib/site-packages/isapi/threaded_extension.py
new file mode 100644
index 0000000..79743ac
--- /dev/null
+++ b/Lib/site-packages/isapi/threaded_extension.py
@@ -0,0 +1,171 @@
+"""An ISAPI extension base class implemented using a thread-pool."""
+# $Id$
+
+import sys
+import time
+from isapi import isapicon, ExtensionError
+import isapi.simple
+from win32file import GetQueuedCompletionStatus, CreateIoCompletionPort, \
+ PostQueuedCompletionStatus, CloseHandle
+from win32security import SetThreadToken
+from win32event import INFINITE
+from pywintypes import OVERLAPPED
+
+import threading
+import traceback
+
+ISAPI_REQUEST = 1
+ISAPI_SHUTDOWN = 2
+
+class WorkerThread(threading.Thread):
+ def __init__(self, extension, io_req_port):
+ self.running = False
+ self.io_req_port = io_req_port
+ self.extension = extension
+ threading.Thread.__init__(self)
+ # We wait 15 seconds for a thread to terminate, but if it fails to,
+ # we don't want the process to hang at exit waiting for it...
+ self.setDaemon(True)
+
+ def run(self):
+ self.running = True
+ while self.running:
+ errCode, bytes, key, overlapped = \
+ GetQueuedCompletionStatus(self.io_req_port, INFINITE)
+ if key == ISAPI_SHUTDOWN and overlapped is None:
+ break
+
+ # Let the parent extension handle the command.
+ dispatcher = self.extension.dispatch_map.get(key)
+ if dispatcher is None:
+ raise RuntimeError("Bad request '%s'" % (key,))
+
+ dispatcher(errCode, bytes, key, overlapped)
+
+ def call_handler(self, cblock):
+ self.extension.Dispatch(cblock)
+
+# A generic thread-pool based extension, using IO Completion Ports.
+# Sub-classes can override one method to implement a simple extension, or
+# may leverage the CompletionPort to queue their own requests, and implement a
+# fully asynch extension.
+class ThreadPoolExtension(isapi.simple.SimpleExtension):
+ "Base class for an ISAPI extension based around a thread-pool"
+ max_workers = 20
+ worker_shutdown_wait = 15000 # 15 seconds for workers to quit...
+ def __init__(self):
+ self.workers = []
+ # extensible dispatch map, for sub-classes that need to post their
+ # own requests to the completion port.
+ # Each of these functions is called with the result of
+ # GetQueuedCompletionStatus for our port.
+ self.dispatch_map = {
+ ISAPI_REQUEST: self.DispatchConnection,
+ }
+
+ def GetExtensionVersion(self, vi):
+ isapi.simple.SimpleExtension.GetExtensionVersion(self, vi)
+ # As per Q192800, the CompletionPort should be created with the number
+ # of processors, even if the number of worker threads is much larger.
+ # Passing 0 means the system picks the number.
+ self.io_req_port = CreateIoCompletionPort(-1, None, 0, 0)
+ # start up the workers
+ self.workers = []
+ for i in range(self.max_workers):
+ worker = WorkerThread(self, self.io_req_port)
+ worker.start()
+ self.workers.append(worker)
+
+ def HttpExtensionProc(self, control_block):
+ overlapped = OVERLAPPED()
+ overlapped.object = control_block
+ PostQueuedCompletionStatus(self.io_req_port, 0, ISAPI_REQUEST, overlapped)
+ return isapicon.HSE_STATUS_PENDING
+
+ def TerminateExtension(self, status):
+ for worker in self.workers:
+ worker.running = False
+ for worker in self.workers:
+ PostQueuedCompletionStatus(self.io_req_port, 0, ISAPI_SHUTDOWN, None)
+ # wait for them to terminate - pity we aren't using 'native' threads
+ # as then we could do a smart wait - but now we need to poll....
+ end_time = time.time() + self.worker_shutdown_wait/1000
+ alive = self.workers
+ while alive:
+ if time.time() > end_time:
+ # xxx - might be nice to log something here.
+ break
+ time.sleep(0.2)
+ alive = [w for w in alive if w.isAlive()]
+ self.dispatch_map = {} # break circles
+ CloseHandle(self.io_req_port)
+
+ # This is the one operation the base class supports - a simple
+ # Connection request. We setup the thread-token, and dispatch to the
+ # sub-class's 'Dispatch' method.
+ def DispatchConnection(self, errCode, bytes, key, overlapped):
+ control_block = overlapped.object
+ # setup the correct user for this request
+ hRequestToken = control_block.GetImpersonationToken()
+ SetThreadToken(None, hRequestToken)
+ try:
+ try:
+ self.Dispatch(control_block)
+ except:
+ self.HandleDispatchError(control_block)
+ finally:
+ # reset the security context
+ SetThreadToken(None, None)
+
+ def Dispatch(self, ecb):
+ """Overridden by the sub-class to handle connection requests.
+
+ This class creates a thread-pool using a Windows completion port,
+ and dispatches requests via this port. Sub-classes can generally
+ implement each connection request using blocking reads and writes, and
+ the thread-pool will still provide decent response to the end user.
+
+ The sub-class can set a max_workers attribute (default is 20). Note
+ that this generally does *not* mean 20 threads will all be concurrently
+ running, via the magic of Windows completion ports.
+
+ There is no default implementation - sub-classes must implement this.
+ """
+ raise NotImplementedError("sub-classes should override Dispatch")
+
+ def HandleDispatchError(self, ecb):
+ """Handles errors in the Dispatch method.
+
+ When a Dispatch method call fails, this method is called to handle
+ the exception. The default implementation formats the traceback
+ in the browser.
+ """
+ ecb.HttpStatusCode = isapicon.HSE_STATUS_ERROR
+ #control_block.LogData = "we failed!"
+ exc_typ, exc_val, exc_tb = sys.exc_info()
+ limit = None
+ try:
+ try:
+ import cgi
+ ecb.SendResponseHeaders("200 OK", "Content-type: text/html\r\n\r\n",
+ False)
+ print(file=ecb)
+ print("
This cast, and knowledge of a specific IID_* to type must be simulated in Python.
+
Python/COM will therefore maintain a map of UID's to Python type objects. Whenever QueryInterface is called, Python will lookup this map, to determine if the object type is supported. If the object is supported, then an object of that type will be returned. If the object is not supported, then a PyIUnknown object will be returned.
+
Note that PyIDispatch will be supported by the core engine. Therefore:
>>> unk=someobj.QueryInterface(SomeUnknownIID) # returns PyIUnknown
+>>> disp=unk.QueryInterface(win32com.IID_Dispatch)
+>>> unk.Release() # Clean up now, rather than waiting for unk death.
+
Is needed to convert to an IDispatch object.
+
Core Support
+
The core COM support module will support the IUnknown, IDispatch, ITypeInfo, ITypeLib and IConnectionPointContainer and IConnectionPoint interfaces. This implies the core COM module supports 6 different OLE client object types, mapped to the 6 IID_*'s representing the objects. (The IConnection* objects allow for Python to repsond to COM events)
+
A psuedo-inheritance scheme is used. The Python types are all derived from the Python IUnknown type (PyIUnknown). Therefore all IUnknown methods are automatically available to all types, just as it should be. The PyIUnknown type manages all object reference counts and destruction.
+
Extensibility
+
To provide the above functionality, a Python map is provided, which maps from a GUID to a Python type object.
+
The advantage of this scheme is an external extension modules can hook into the core support. For example, imagine the following code:
+
>>> import myextracom # external .pyd supporting some interface.
+# myextracom.pyd will do the equivilent of
Would correctly return an object defined in the extension module.
+
Server Framework
+
General Framework
+
A server framework has been put in place which provides the following features:
+
All Interfaces provide VTBL support - this means that the Servers exposed by Python are callable from C++ and other compiled languages.
+
Supports full "inproc" servers. This means that no external .EXE is needed making Python COM servers available in almost all cases.
+
An extensible model which allows for extension modules to provide server support for interfaces defined in that module. A map is provided which maps from a GUID to a function pointer which creates the interface.
+
Python and Variant Types Conversion
+
In general, Python and COM are both "type-less". COM is type-less via the VARIANT object, which supports many types, and Python is type-less due to its object model.
+
There are a number of areas where Python and OLE clash.
+
Parameters and conversions.
+
For simple calls, there are 2 helpers available which will convert to and from PyObjects and VARIANTS. The call to convert a Python object to a VARIANT is simple in that it returns a VARIANT of the most appropriate type for the Python object - ie, the type of the Python object determines the resulting VARIANT type.
+
There are also more complex conversion routines available, wrapped in a C++ helper class. Typically, these helpers are used whenever a specific variant type is known (eg, when an ITypeInfo is available for the object being used). In this case, all efforts are made to convert the Python type to the requested variant type - ie, in this situation, the VARIANT type determines how the Python object is coerced. In addition, this code supports the use of "ByRef" and pointer paramaters, providing and freeing any buffers necessary for the call.
+
diff --git a/Lib/site-packages/win32com/HTML/QuickStartClientCom.html b/Lib/site-packages/win32com/HTML/QuickStartClientCom.html
new file mode 100644
index 0000000..a5c3ca9
--- /dev/null
+++ b/Lib/site-packages/win32com/HTML/QuickStartClientCom.html
@@ -0,0 +1,82 @@
+
+
+
+
+Quick Start to Client side COM and Python
+
+
+
+
+
Quick Start to Client side COM and Python
+
Introduction
+
This documents how to quickly start using COM from Python. It is not a thorough discussion of the COM system, or of the concepts introduced by COM.
o = win32com.client.Dispatch("Excel.Application")
+o.Visible = 1
+o.Workbooks.Add() # for office 97 – 95 a bit different!
+o.Cells(1,1).Value = "Hello"
+
And we will see the word "Hello" appear in the top cell.
Good question. This is hard! You need to use the documentation with the products, or possibly a COM browser. Note however that COM browsers typically rely on these objects registering themselves in certain ways, and many objects to not do this. You are just expected to know.
+
The Python COM browser
+
PythonCOM comes with a basic COM browser that may show you the information you need. Note that this package requires Pythonwin (ie, the MFC GUI environment) to be installed for this to work.
+
There are far better COM browsers available - I tend to use the one that comes with MSVC, or this one!
+
To run the browser, simply select it from the Pythonwin Tools menu, or double-click on the file win32com\client\combrowse.py
In the above examples, if we printed the 'repr(o)' object above, it would have resulted in
+
<COMObject Excel.Application>
+
This reflects that the object is a generic COM object that Python has no special knowledge of (other than the name you used to create it!). This is known as a "dynamic dispatch" object, as all knowledge is built dynamically. The win32com package also has the concept of static dispatch objects, which gives Python up-front knowledge about the objects that it is working with (including arguments, argument types, etc)
+
In a nutshell, Static Dispatch involves the generation of a .py file that contains support for the specific object. For more overview information, please see the documentation references above.
+
The generation and management of the .py files is somewhat automatic, and involves one of 2 steps:
+
+
+
Using makepy.py to select a COM library. This process is very similar to Visual Basic, where you select from a list of all objects installed on your system, and once selected the objects are magically useable.
+
+
or
+
+
+
Use explicit code to check for, and possibly generate, support at run-time. This is very powerful, as it allows the developer to avoid ensuring the user has selected the appropriate type library. This option is extremely powerful for OCX users, as it allows Python code to sub-class an OCX control, but the actual sub-class can be generated at run-time. Use makepy.py with a -i option to see how to include this support in your Python code.
+
+
The win32com.client.gencache module manages these generated files. This module has some documentation of its own, but you probably don't need to know the gory details!
+
How do I get at the generated module?
+
You will notice that the generated file name is long and cryptic - obviously not designed for humans to work with! So how do you get at the module object for the generated code?
+
Hopefully, the answer is you shouldn't need to. All generated file support is generally available directly via win32com.client.Dispatch and win32com.client.constants. But should you ever really need the Python module object, the win32com.client.gencache module has functions specifically for this. The functions GetModuleForCLSID and GetModuleForProgID both return Python module objects that you can use in your code. See the docstrings in the gencache code for more details.
+
To generate Python Sources supporting a COM object
+
Example using Microsoft Office 97.
+
Either:
+
+
+
Run 'win32com\client\makepy.py' (eg, run it from the command window, or double-click on it) and a list will be presented. Select the Type Library 'Microsoft Word 8.0 Object Library'
+
From a command prompt, run the command 'makepy.py "Microsoft Word 8.0 Object Library"' (include the double quotes). This simply avoids the selection process.
+
If you desire, you can also use explicit code to generate it just before you need to use it at runtime. Run 'makepy.py -i "Microsoft Word 8.0 Object Library"' (include the double quotes) to see how to do this.
+
+
And that is it! Nothing more needed. No special import statements needed! Now, you simply need say
Makepy automatically installs all generated constants from a type library in an object called win32com.clients.constants. You do not need to do anything special to make these constants work, other than create the object itself (ie, in the example above, the constants relating to Word would automatically be available after the w=win32com.client.Dispatch("Word.Application") statement.
+
For example, immediately after executing the code above, you could execute the following:
+
diff --git a/Lib/site-packages/win32com/HTML/QuickStartServerCom.html b/Lib/site-packages/win32com/HTML/QuickStartServerCom.html
new file mode 100644
index 0000000..cafaff9
--- /dev/null
+++ b/Lib/site-packages/win32com/HTML/QuickStartServerCom.html
@@ -0,0 +1,195 @@
+
+
+
+
+Quick Start to Server Side COM and Python
+
+
+
+
+
+
+
Quick Start to Server side COM and Python
+
Introduction
+
This documents how to quickly start implementing COM objects in Python. It is not a thorough discussion of the COM system, or of the concepts introduced by COM.
This is obviously a very simple server. In particular, custom error handling would be needed for a production class server. In addition, there are some contrived properties just for demonstration purposes.
+
Make Unicode concessions
+
At this stage, Python and Unicode don’t really work well together. All strings which come from COM will actually be Unicode objects rather than string objects.
+
To make this code work in a COM environment, the last line of the "Hello" method must become:
+
+
+
+
+
return "Hello" + " " * self.softspace + str(who)
+
+
+
+
+
Note the conversion of the "who" to "str(who)". This forces the Unicode object into a native Python string object.
+
For details on how to debug COM Servers to find this sort of error, please see debugging the class
+
Annotate the class with win32com specific attributes
+
This is not a complete list of names, simply a list of properties used by this sample.
+
+
+
Property Name
+
+
Description
+
+
+
_public_methods_
+
+
List of all method names exposed to remote COM clients
+
+
+
_public_attrs_
+
+
List of all attribute names exposed to remote COM clients
+
+
+
_readonly_attrs_
+
+
List of all attributes which can be accessed, but not set.
COM requires that all objects use a unique CLSID and be registered under a "user friendly" name. This documents the process.
+
Generating the CLSID
+
Microsoft Visual C++ comes with various tools for generating CLSID's, which are quite suitable. Alternatively, the pythoncom module exports the function CreateGuid() to generate these identifiers.
Obviously the GUID that you get will be different than that displayed here.
+
Preparing for registration of the Class
+
The win32com package allows yet more annotations to be applied to a class, allowing registration to be effected with 2 lines in your source file. The registration annotations used by this sample are:
+
+
+
Property Name
+
+
Description
+
+
+
_reg_clsid_
+
+
The CLSID of the COM object
+
+
+
_reg_progid_
+
+
The "program ID", or Name, of the COM Server. This is the name the user usually uses to instantiate the object
+
+
+
_reg_desc_
+
+
Optional: The description of the COM Server. Used primarily for COM browsers. If not specified, the _reg_progid_ is used as the description.
+
+
+
_reg_class_spec_
+
+
Optional: A string which represents how Python can create the class instance. The string is of format
+[package.subpackage.]module.class
+
The portion up to the class name must be valid for Python to "import", and the class portion must be a valid attribute in the specified class.
+
This is optional from build 124 of Pythoncom., and has been removed from this sample.
+
+
+
_reg_remove_keys_
+
+
Optional: A list of tuples of extra registry keys to be removed when uninstalling the server. Each tuple is of format ("key", root), where key is a string, and root is one of the win32con.HKEY_* constants (this item is optional, defaulting to HKEY_CLASSES_ROOT)
+
+
+
+
Note there are quite a few other keys available. Also note that these annotations are not required - they just make registration simple. Helper functions in the module win32com.server.register allow you to explicitly specify each of these attributes without attaching them to the class.
The idiom that most Python COM Servers use is that they register themselves when run as a script (ie, when executed from the command line.) Thus the standard "if __name__=='__main___':" technique works well.
+
win32com.server.register contains a number of helper functions. The easiest to use is "UseCommandLine".
+
Registration becomes as simple as:
+
if __name__=='__main__':
+ # ni only for 1.4!
+ import ni, win32com.server.register
+ win32com.server.register.UseCommandLine(HelloWorld)
For the purposes of this demonstration, we will test the class using Visual Basic. This code should run under any version of Visual Basic, including VBA found in Microsoft Office. Any COM compliant package could be used alternatively. VB has been used just to prove there is no "smoke and mirrors. For information on how to test the server using Python, please see the Quick Start to Client side COM documentation.
+
This is not a tutorial in VB. The code is just presented! Run it, and it will work!
When things go wrong in COM Servers, there is often nowhere useful for the Python traceback to go, even if such a traceback is generated.
+
Rather than discuss how it works, I will just present the procedure to debug your server:
+
To register a debug version of your class, run the script (as above) but pass in a "--debug" parameter. Eg, for the server above, use the command line "testcomserver.py --debug".
+
To see the debug output generated (and any print statements you may choose to add!) you can simply select the "Remote Debug Trace Collector" from the Pythonwin Tools menu, or run the script "win32traceutil.py" from Windows Explorer or a Command Prompt.
Servers need to be able to provide exception information to their client. In some cases, it may be a simple return code (such as E_NOTIMPLEMENTED), but often it can contain much richer information, describing the error on detail, and even a help file and topic where more information can be found.
+
We use Python class based exceptions to provide this information. The COM framework will examine the exception, and look for certain known attributes. These attributes will be copied across to the COM exception, and passed back to the client.
+
The following attributes are supported, and correspond to the equivalent entry in the COM Exception structure:
+scode, code, description, source, helpfile and helpcontext
+
To make working with exceptions easier, there is a helper module "win32com.server.exception.py", which defines a single class. An example of its usage would be:
+
raise COMException(desc="Must be a string",scode=winerror.E_INVALIDARG,helpfile="myhelp.hlp",...)
+
(Note the COMException class supports (and translates) "desc" as a shortcut for "description", but the framework requires "description")
This is information about how it all hangs together. The casual COM author need not know this.
+
Whenever a Python Server needs to be created, the C++ framework first instantiates a "policy" object. This "policy" object is the gatekeeper for the COM Server - it is responsible for creating the underlying Python object that is the server (ie, your object), and also for translating the underlying COM requests for the object.
+
This policy object handles all of the underlying COM functionality. For example, COM requires all methods and properties to have unique numeric ID's associated with them. The policy object manages the creation of these ID's for the underlying Python methods and attributes. Similarly, when the client whishes to call a method with ID 123, the policy object translates this back to the actual method, and makes the call.
+
It should be noted that the operation of the "policy" object could be dictated by the Python object - the policy object has many defaults, but the actual Python class can always dictate its operation.
+
Default Policy attributes
+
The default policy object has a few special attributes that define who the object is exposed to COM. The example above shows the _public_methods attribute, but this section describes all such attributes in detail.
+
_public_methods_
+
Required list of strings, containing the names of all methods to be exposed to COM. It is possible this will be enhanced in the future (eg, possibly '*' will be recognised to say all methods, or some other ideas…)
+
_public_attrs_
+
Optional list of strings containing all attribute names to be exposed, both for reading and writing. The attribute names must be valid instance variables.
+
_readonly_attrs_
+
Optional list of strings defining the name of attributes exposed read-only.
+
_com_interfaces_
+
Optional list of IIDs exposed by this object. If this attribute is missing, IID_IDispatch is assumed (ie, if not supplied, the COM object will be created as a normal Automation object.
+
diff --git a/Lib/site-packages/win32com/HTML/image/BTN_HomePage.gif b/Lib/site-packages/win32com/HTML/image/BTN_HomePage.gif
new file mode 100644
index 0000000..5767fa0
Binary files /dev/null and b/Lib/site-packages/win32com/HTML/image/BTN_HomePage.gif differ
diff --git a/Lib/site-packages/win32com/HTML/image/BTN_ManualTop.gif b/Lib/site-packages/win32com/HTML/image/BTN_ManualTop.gif
new file mode 100644
index 0000000..4200880
Binary files /dev/null and b/Lib/site-packages/win32com/HTML/image/BTN_ManualTop.gif differ
diff --git a/Lib/site-packages/win32com/HTML/image/BTN_NextPage.gif b/Lib/site-packages/win32com/HTML/image/BTN_NextPage.gif
new file mode 100644
index 0000000..3d055fe
Binary files /dev/null and b/Lib/site-packages/win32com/HTML/image/BTN_NextPage.gif differ
diff --git a/Lib/site-packages/win32com/HTML/image/BTN_PrevPage.gif b/Lib/site-packages/win32com/HTML/image/BTN_PrevPage.gif
new file mode 100644
index 0000000..955eae6
Binary files /dev/null and b/Lib/site-packages/win32com/HTML/image/BTN_PrevPage.gif differ
diff --git a/Lib/site-packages/win32com/HTML/image/blank.gif b/Lib/site-packages/win32com/HTML/image/blank.gif
new file mode 100644
index 0000000..a58ecfe
Binary files /dev/null and b/Lib/site-packages/win32com/HTML/image/blank.gif differ
diff --git a/Lib/site-packages/win32com/HTML/image/pycom_blowing.gif b/Lib/site-packages/win32com/HTML/image/pycom_blowing.gif
new file mode 100644
index 0000000..0d65a29
Binary files /dev/null and b/Lib/site-packages/win32com/HTML/image/pycom_blowing.gif differ
diff --git a/Lib/site-packages/win32com/HTML/image/pythoncom.gif b/Lib/site-packages/win32com/HTML/image/pythoncom.gif
new file mode 100644
index 0000000..6b5020a
Binary files /dev/null and b/Lib/site-packages/win32com/HTML/image/pythoncom.gif differ
diff --git a/Lib/site-packages/win32com/HTML/image/www_icon.gif b/Lib/site-packages/win32com/HTML/image/www_icon.gif
new file mode 100644
index 0000000..4239d2c
Binary files /dev/null and b/Lib/site-packages/win32com/HTML/image/www_icon.gif differ
diff --git a/Lib/site-packages/win32com/HTML/index.html b/Lib/site-packages/win32com/HTML/index.html
new file mode 100644
index 0000000..0e8cefe
--- /dev/null
+++ b/Lib/site-packages/win32com/HTML/index.html
@@ -0,0 +1,31 @@
+
+
+
+
+win32com
+
+
+
+
+
+
+
+
Python and COM
+
Introduction
+
Python has an excellent interface to COM (also known variously as OLE2, ActiveX, etc).
+
The Python COM package can be used to interface to almost any COM program (such as the MS-Office suite), write servers that can be hosted by any COM client (such as Visual Basic or C++), and has even been used to provide the core ActiveX Scripting Support.
Misc stuff I don’t know where to put anywhere else
+
Client Side Dispatch
+
Using win32com.client.Dispatch automatically invokes all the win32com client side "smarts", including automatic usage of generated .py files etc.
+
If you wish to avoid that, and use truly "dynamic" objects (ie, there is generated .py support available, but you wish to avoid it), you can use win32com.client.dynamic.Dispatch
+
_print_details_() method
+If win32com.client.dynamic.Dispatch is used, the objects have a _print_details_() method available, which prints all relevant knowledge about an object (for example, all methods and properties). For objects that do not expose runtime type information, _print_details_ may not list anything.
This document describes the win32com package in general terms.
+
The COM support can be thought of as existing in 2 main portions - the C++ support code (the core PythonCOM module), and helper code, implemented in Python. The total package is known as "win32com".
+
The win32com support is stand-alone. It does not require Pythonwin.
+
The win32com package
+
To facilitate an orderly framework, the Python "ni" module has been used, and the entire package is known as "win32com". As is normal for such packages, win32com itself does not provide any functionality. Some of the modules are described below:
+
+
+
win32com.pythoncom - core C++ support
.
+This module is rarely used directly by programmers - instead the other "helper" module are used, which themselves draw on the core pythoncom services.
+
win32com.client package
+
Support for COM clients used by Python. Some of the modules in this package allow for dynamic usage of COM clients, a module for generating .py files for certain COM servers, etc.
+
win32com.server package
+
Support for COM servers written in Python. The modules in this package provide most of the underlying framework for magically turning Python classes into COM servers, exposing the correct public methods, registering your server in the registry, etc.
+
win32com.axscript
+
ActiveX Scripting implementation for Python.
+
win32com.axdebug
+
Active Debugging implementation for Python
+
win32com.mapi
+
Utilities for working with MAPI and the Microsoft Exchange Server
+
+
+
The pythoncom module
+
The pythoncom module is the underlying C++ support for all COM related objects. In general, Python programmers will not use this module directly, but use win32com helper classes and functions.
+
This module exposes a C++ like interface to COM - there are objects implemented in pythoncom that have methods "QueryInterface()", "Invoke()", just like the C++ API. If you are using COM in C++, you would not call a method directly, you would use pObject->Invoke( …, MethodId, argArray…). Similarly, if you are using pythoncom directly, you must also use the Invoke method to call an object's exposed method.
+
There are some Python wrappers for hiding this raw interface, meaning you should almost never need to use the pythoncom module directly. These helpers translate a "natural" looking interface (eg, obj.SomeMethod()) into the underlying Invoke call.
+win32com attempts to provide a seamless COM interface and hide many COM
+implementation details, including the use of COM VARIANT structures. This
+means that in most cases, you just call a COM object using normal Python
+objects as parameters and get back normal Python objects as results.
+
+
+
+However, in some cases this doesn't work very well, particularly when using
+"dynamic" (aka late-bound) objects, or when using "makepy" (aka early-bound)
+objects which only declare a parameter is a VARIANT.
+
+
+
+The win32com.client.VARIANT object is designed to overcome these
+problems.
+
+
+
Drawbacks
+The primary issue with this approach is that the programmer must learn more
+about COM VARIANTs than otherwise - they need to know concepts such as
+variants being byref, holding arrays, or that some may hold 32bit
+unsigned integers while others hold 64bit signed ints, and they need to
+understand this in the context of a single method call. In short, this is
+a relatively advanced feature. The good news though is that use of these
+objects should never cause your program to hard-crash - the worst you should
+expect are Python or COM exceptions being thrown.
+
+
The VARIANT object
+
+The VARIANT object lives in win32com.client. The constructor
+takes 2 parameters - the 'variant type' and the value. The 'variant type' is
+an integer and can be one or more of the pythoncom.VT_* values,
+possibly or'd together.
+
+
For example, to create a VARIANT object which defines a byref array of
+32bit integers, you could use:
+
+
+>>> from win32com.client import VARIANT
+>>> import pythoncom
+>>> v = VARIANT(pythoncom.VT_BYREF | pythoncom.VT_ARRAY | pythoncom.VT_I4,
+... [1,2,3,4])
+>>> v
+win32com.client.VARIANT(24579, [1, 2, 3, 4])
+>>>
+
+
+This variable can then be used whereever a COM VARIANT is expected.
+
+
Example usage with dynamic objects.
+
+For this example we will use the COM object used for win32com testing,
+PyCOMTest.PyCOMTest. This object defines a method which is
+defined in IDL as:
+
+HRESULT DoubleInOutString([in,out] BSTR *str);
+
+
+As you can see, it takes a single string parameter which is also used as
+an "out" parameter - the single parameter will be updated after the call.
+The implementation of the method simply "doubles" the string.
+
+
If the object has a type-library, this method works fine with makepy
+generated support. For example:
+
+
+>>> from win32com.client.gencache import EnsureDispatch
+>>> ob = EnsureDispatch("PyCOMTest.PyCOMTest")
+>>> ob.DoubleInOutString("Hello")
+u'HelloHello'
+>>>
+
+
+However, if makepy support is not available the method does not work as
+expected. For the next example we will use DumbDispatch to
+simulate the object not having a type-library.
+
+
+
+As you can see, no result came back from the function. This is because
+win32com has no type information available to use, so doesn't know the
+parameter should be passed as a byref parameter. To work
+around this, we can use the VARIANT object.
+
+
The following example explicitly creates a VARIANT object with a
+variant type of a byref string and a value 'Hello'. After making the
+call with this VARIANT the value is updated.
+
+
+>>> import win32com.client.dynamic
+>>> from win32com.client import VARIANT
+>>> import pythoncom
+>>> ob = win32com.client.dynamic.DumbDispatch("PyCOMTest.PyCOMTest")
+>>> variant = VARIANT(pythoncom.VT_BYREF | pythoncom.VT_BSTR, "Hello")
+>>> variant.value # check the value before the call.
+'Hello'
+>>> ob.DoubleInOutString(variant)
+>>> variant.value
+u'HelloHello'
+>>>
+
+
+
Usage with generated objects
+
+In most cases, objects with makepy support (ie, 'generated' objects) don't
+need to use the VARIANT object - the type information means win32com can guess
+the right thing to pass. However, in some cases the VARIANT object can still
+be useful.
+
+Imagine a poorly specified object with IDL like:
+
+
+HRESULT DoSomething([in] VARIANT value);
+
+
+But also imagine that the object has a limitation that if the parameter is an
+integer, it must be a 32bit unsigned value - any other integer representation
+will fail.
+
+
If you just pass a regular Python integer to this function, it will
+generally be passed as a 32bit signed integer and given the limitation above,
+will fail. The VARIANT object allows you to work around the limitation - just
+create a variant object VARIANT(pythoncom.VT_UI4, int_value) and
+pass that - the function will then be called with the explicit type you
+specified and will succeed.
+
+
Note that you can not use a VARIANT object to override the types described
+in a type library. If a makepy generated class specifies that a VT_UI2 is
+expected, attempting to pass a VARIANT object will fail. In this case you
+would need to hack around the problem. For example, imagine ob
+was a COM object which a method called foo and you wanted to
+override the type declaration for foo by passing a VARIANT.
+You could do something like:
+
+
+
+The code above converts the makepy supported ob into a
+'dumb' (ie, non-makepy supported) version of the object, which will then
+allow you to use VARIANT objects for the problematic methods.
+
+
+
diff --git a/Lib/site-packages/win32com/License.txt b/Lib/site-packages/win32com/License.txt
new file mode 100644
index 0000000..237b6c9
--- /dev/null
+++ b/Lib/site-packages/win32com/License.txt
@@ -0,0 +1,30 @@
+Unless stated in the specfic source file, this work is
+Copyright (c) 1996-2008, Greg Stein and Mark Hammond.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in
+the documentation and/or other materials provided with the distribution.
+
+Neither names of Greg Stein, Mark Hammond nor the name of contributors may be used
+to endorse or promote products derived from this software without
+specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
+IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Lib/site-packages/win32com/__init__.py b/Lib/site-packages/win32com/__init__.py
new file mode 100644
index 0000000..a363017
--- /dev/null
+++ b/Lib/site-packages/win32com/__init__.py
@@ -0,0 +1,120 @@
+#
+# Initialization for the win32com package
+#
+
+import win32api, sys, os
+import pythoncom
+
+# flag if we are in a "frozen" build.
+_frozen = getattr(sys, "frozen", 1==0)
+# pythoncom dumbly defaults this to zero - we believe sys.frozen over it.
+if _frozen and not getattr(pythoncom, "frozen", 0):
+ pythoncom.frozen = sys.frozen
+
+# Add support for an external "COM Extensions" path.
+# Concept is that you can register a seperate path to be used for
+# COM extensions, outside of the win32com directory. These modules, however,
+# look identical to win32com built-in modules.
+# This is the technique that we use for the "standard" COM extensions.
+# eg "win32com.mapi" or "win32com.axscript" both work, even though they do not
+# live under the main win32com directory.
+__gen_path__ = ''
+__build_path__ = None
+### TODO - Load _all_ \\Extensions subkeys - for now, we only read the default
+### Modules will work if loaded into "win32comext" path.
+
+def SetupEnvironment():
+ HKEY_LOCAL_MACHINE = -2147483646 # Avoid pulling in win32con for just these...
+ KEY_QUERY_VALUE = 0x1
+ # Open the root key once, as this is quite slow on NT.
+ try:
+ keyName = "SOFTWARE\\Python\\PythonCore\\%s\\PythonPath\\win32com" % sys.winver
+ key = win32api.RegOpenKey(HKEY_LOCAL_MACHINE , keyName, 0, KEY_QUERY_VALUE)
+ except (win32api.error, AttributeError):
+ key = None
+
+ try:
+ found = 0
+ if key is not None:
+ try:
+ __path__.append( win32api.RegQueryValue(key, "Extensions" ))
+ found = 1
+ except win32api.error:
+ # Nothing registered
+ pass
+ if not found:
+ try:
+ __path__.append( win32api.GetFullPathName( __path__[0] + "\\..\\win32comext") )
+ except win32api.error:
+ # Give up in disgust!
+ pass
+
+ # For the sake of developers, we also look up a "BuildPath" key
+ # If extension modules add support, we can load their .pyd's from a completely
+ # different directory (see the comments below)
+ try:
+ if key is not None:
+ global __build_path__
+ __build_path__ = win32api.RegQueryValue(key, "BuildPath")
+ __path__.append(__build_path__)
+ except win32api.error:
+ # __build_path__ neednt be defined.
+ pass
+ global __gen_path__
+ if key is not None:
+ try:
+ __gen_path__ = win32api.RegQueryValue(key, "GenPath")
+ except win32api.error:
+ pass
+ finally:
+ if key is not None:
+ key.Close()
+
+# A Helper for developers. A sub-package's __init__ can call this help function,
+# which allows the .pyd files for the extension to live in a special "Build" directory
+# (which the win32com developers do!)
+def __PackageSupportBuildPath__(package_path):
+ # See if we have a special directory for the binaries (for developers)
+ if not _frozen and __build_path__:
+ package_path.append(__build_path__)
+
+if not _frozen:
+ SetupEnvironment()
+
+# If we don't have a special __gen_path__, see if we have a gen_py as a
+# normal module and use that (ie, "win32com.gen_py" may already exist as
+# a package.
+if not __gen_path__:
+ try:
+ import win32com.gen_py
+ # hrmph - 3.3 throws: TypeError: '_NamespacePath' object does not support indexing
+ # attempting to get __path__[0] - but I can't quickly repro this stand-alone.
+ # Work around it by using an iterator.
+ __gen_path__ = next(iter(sys.modules["win32com.gen_py"].__path__))
+ except ImportError:
+ # If a win32com\gen_py directory already exists, then we use it
+ # (gencache doesn't insist it have an __init__, but our __import__
+ # above does!
+ __gen_path__ = os.path.abspath(os.path.join(__path__[0], "gen_py"))
+ if not os.path.isdir(__gen_path__):
+ # We used to dynamically create a directory under win32com -
+ # but this sucks. If the dir doesn't already exist, we we
+ # create a version specific directory under the user temp
+ # directory.
+ __gen_path__ = os.path.join(
+ win32api.GetTempPath(), "gen_py",
+ "%d.%d" % (sys.version_info[0], sys.version_info[1]))
+
+# we must have a __gen_path__, but may not have a gen_py module -
+# set that up.
+if "win32com.gen_py" not in sys.modules:
+ # Create a "win32com.gen_py", but with a custom __path__
+ import imp
+ gen_py = imp.new_module("win32com.gen_py")
+ gen_py.__path__ = [ __gen_path__ ]
+ sys.modules[gen_py.__name__]=gen_py
+ del imp
+gen_py = sys.modules["win32com.gen_py"]
+
+# get rid of these for module users
+del os, sys, win32api, pythoncom
diff --git a/Lib/site-packages/win32com/client/CLSIDToClass.py b/Lib/site-packages/win32com/client/CLSIDToClass.py
new file mode 100644
index 0000000..6a33396
--- /dev/null
+++ b/Lib/site-packages/win32com/client/CLSIDToClass.py
@@ -0,0 +1,53 @@
+"""Manages a dictionary of CLSID strings to Python classes.
+
+Primary use of this module is to allow modules generated by
+makepy.py to share classes. @makepy@ automatically generates code
+which interacts with this module. You should never need to reference
+this module directly.
+
+This module only provides support for modules which have been previously
+been imported. The gencache module provides some support for loading modules
+on demand - once done, this module supports it...
+
+As an example, the MSACCESS.TLB type library makes reference to the
+CLSID of the Database object, as defined in DAO3032.DLL. This
+allows code using the MSAccess wrapper to natively use Databases.
+
+This obviously applies to all cooperating objects, not just DAO and
+Access.
+"""
+mapCLSIDToClass = {}
+
+def RegisterCLSID( clsid, pythonClass ):
+ """Register a class that wraps a CLSID
+
+ This function allows a CLSID to be globally associated with a class.
+ Certain module will automatically convert an IDispatch object to an
+ instance of the associated class.
+ """
+
+ mapCLSIDToClass[str(clsid)] = pythonClass
+
+def RegisterCLSIDsFromDict( dict ):
+ """Register a dictionary of CLSID's and classes.
+
+ This module performs the same function as @RegisterCLSID@, but for
+ an entire dictionary of associations.
+
+ Typically called by makepy generated modules at import time.
+ """
+ mapCLSIDToClass.update(dict)
+
+def GetClass(clsid):
+ """Given a CLSID, return the globally associated class.
+
+ clsid -- a string CLSID representation to check.
+ """
+ return mapCLSIDToClass[clsid]
+
+def HasClass(clsid):
+ """Determines if the CLSID has an associated class.
+
+ clsid -- the string CLSID to check
+ """
+ return clsid in mapCLSIDToClass
diff --git a/Lib/site-packages/win32com/client/__init__.py b/Lib/site-packages/win32com/client/__init__.py
new file mode 100644
index 0000000..d801505
--- /dev/null
+++ b/Lib/site-packages/win32com/client/__init__.py
@@ -0,0 +1,539 @@
+# This module exists to create the "best" dispatch object for a given
+# object. If "makepy" support for a given object is detected, it is
+# used, otherwise a dynamic dispatch object.
+
+# Note that if the unknown dispatch object then returns a known
+# dispatch object, the known class will be used. This contrasts
+# with dynamic.Dispatch behaviour, where dynamic objects are always used.
+
+import pythoncom
+from . import dynamic
+from . import gencache
+import sys
+import pywintypes
+
+_PyIDispatchType = pythoncom.TypeIIDs[pythoncom.IID_IDispatch]
+
+
+def __WrapDispatch(dispatch, userName = None, resultCLSID = None, typeinfo = None, \
+ UnicodeToString=None, clsctx = pythoncom.CLSCTX_SERVER,
+ WrapperClass = None):
+ """
+ Helper function to return a makepy generated class for a CLSID if it exists,
+ otherwise cope by using CDispatch.
+ """
+ assert UnicodeToString is None, "this is deprecated and will go away"
+ if resultCLSID is None:
+ try:
+ typeinfo = dispatch.GetTypeInfo()
+ if typeinfo is not None: # Some objects return NULL, some raise exceptions...
+ resultCLSID = str(typeinfo.GetTypeAttr()[0])
+ except (pythoncom.com_error, AttributeError):
+ pass
+ if resultCLSID is not None:
+ from . import gencache
+ # Attempt to load generated module support
+ # This may load the module, and make it available
+ klass = gencache.GetClassForCLSID(resultCLSID)
+ if klass is not None:
+ return klass(dispatch)
+
+ # Return a "dynamic" object - best we can do!
+ if WrapperClass is None: WrapperClass = CDispatch
+ return dynamic.Dispatch(dispatch, userName, WrapperClass, typeinfo, clsctx=clsctx)
+
+
+def GetObject(Pathname = None, Class = None, clsctx = None):
+ """
+ Mimic VB's GetObject() function.
+
+ ob = GetObject(Class = "ProgID") or GetObject(Class = clsid) will
+ connect to an already running instance of the COM object.
+
+ ob = GetObject(r"c:\blah\blah\foo.xls") (aka the COM moniker syntax)
+ will return a ready to use Python wrapping of the required COM object.
+
+ Note: You must specifiy one or the other of these arguments. I know
+ this isn't pretty, but it is what VB does. Blech. If you don't
+ I'll throw ValueError at you. :)
+
+ This will most likely throw pythoncom.com_error if anything fails.
+ """
+ if clsctx is None:
+ clsctx = pythoncom.CLSCTX_ALL
+
+ if (Pathname is None and Class is None) or \
+ (Pathname is not None and Class is not None):
+ raise ValueError("You must specify a value for Pathname or Class, but not both.")
+
+ if Class is not None:
+ return GetActiveObject(Class, clsctx)
+ else:
+ return Moniker(Pathname, clsctx)
+
+def GetActiveObject(Class, clsctx = pythoncom.CLSCTX_ALL):
+ """
+ Python friendly version of GetObject's ProgID/CLSID functionality.
+ """
+ resultCLSID = pywintypes.IID(Class)
+ dispatch = pythoncom.GetActiveObject(resultCLSID)
+ dispatch = dispatch.QueryInterface(pythoncom.IID_IDispatch)
+ return __WrapDispatch(dispatch, Class, resultCLSID = resultCLSID, clsctx = clsctx)
+
+def Moniker(Pathname, clsctx = pythoncom.CLSCTX_ALL):
+ """
+ Python friendly version of GetObject's moniker functionality.
+ """
+ moniker, i, bindCtx = pythoncom.MkParseDisplayName(Pathname)
+ dispatch = moniker.BindToObject(bindCtx, None, pythoncom.IID_IDispatch)
+ return __WrapDispatch(dispatch, Pathname, clsctx=clsctx)
+
+def Dispatch(dispatch, userName = None, resultCLSID = None, typeinfo = None, UnicodeToString=None, clsctx = pythoncom.CLSCTX_SERVER):
+ """Creates a Dispatch based COM object.
+ """
+ assert UnicodeToString is None, "this is deprecated and will go away"
+ dispatch, userName = dynamic._GetGoodDispatchAndUserName(dispatch,userName,clsctx)
+ return __WrapDispatch(dispatch, userName, resultCLSID, typeinfo, clsctx=clsctx)
+
+def DispatchEx(clsid, machine=None, userName = None, resultCLSID = None, typeinfo = None, UnicodeToString=None, clsctx = None):
+ """Creates a Dispatch based COM object on a specific machine.
+ """
+ assert UnicodeToString is None, "this is deprecated and will go away"
+ # If InProc is registered, DCOM will use it regardless of the machine name
+ # (and regardless of the DCOM config for the object.) So unless the user
+ # specifies otherwise, we exclude inproc apps when a remote machine is used.
+ if clsctx is None:
+ clsctx = pythoncom.CLSCTX_SERVER
+ if machine is not None: clsctx = clsctx & ~pythoncom.CLSCTX_INPROC
+ if machine is None:
+ serverInfo = None
+ else:
+ serverInfo = (machine,)
+ if userName is None: userName = clsid
+ dispatch = pythoncom.CoCreateInstanceEx(clsid, None, clsctx, serverInfo, (pythoncom.IID_IDispatch,))[0]
+ return Dispatch(dispatch, userName, resultCLSID, typeinfo, clsctx=clsctx)
+
+class CDispatch(dynamic.CDispatch):
+ """
+ The dynamic class used as a last resort.
+ The purpose of this overriding of dynamic.CDispatch is to perpetuate the policy
+ of using the makepy generated wrapper Python class instead of dynamic.CDispatch
+ if/when possible.
+ """
+ def _wrap_dispatch_(self, ob, userName = None, returnCLSID = None, UnicodeToString=None):
+ assert UnicodeToString is None, "this is deprecated and will go away"
+ return Dispatch(ob, userName, returnCLSID,None)
+
+def CastTo(ob, target):
+ """'Cast' a COM object to another interface"""
+ # todo - should support target being an IID
+ if hasattr(target, "index"): # string like
+ # for now, we assume makepy for this to work.
+ if "CLSID" not in ob.__class__.__dict__:
+ # Eeek - no makepy support - try and build it.
+ ob = gencache.EnsureDispatch(ob)
+ if "CLSID" not in ob.__class__.__dict__:
+ raise ValueError("Must be a makepy-able object for this to work")
+ clsid = ob.CLSID
+ # Lots of hoops to support "demand-build" - ie, generating
+ # code for an interface first time it is used. We assume the
+ # interface name exists in the same library as the object.
+ # This is generally the case - only referenced typelibs may be
+ # a problem, and we can handle that later. Maybe
+ # So get the generated module for the library itself, then
+ # find the interface CLSID there.
+ mod = gencache.GetModuleForCLSID(clsid)
+ # Get the 'root' module.
+ mod = gencache.GetModuleForTypelib(mod.CLSID, mod.LCID,
+ mod.MajorVersion, mod.MinorVersion)
+ # Find the CLSID of the target
+ target_clsid = mod.NamesToIIDMap.get(target)
+ if target_clsid is None:
+ raise ValueError("The interface name '%s' does not appear in the " \
+ "same library as object '%r'" % (target, ob))
+ mod = gencache.GetModuleForCLSID(target_clsid)
+ target_class = getattr(mod, target)
+ # resolve coclass to interface
+ target_class = getattr(target_class, "default_interface", target_class)
+ return target_class(ob) # auto QI magic happens
+ raise ValueError
+
+class Constants:
+ """A container for generated COM constants.
+ """
+ def __init__(self):
+ self.__dicts__ = [] # A list of dictionaries
+ def __getattr__(self, a):
+ for d in self.__dicts__:
+ if a in d:
+ return d[a]
+ raise AttributeError(a)
+
+# And create an instance.
+constants = Constants()
+
+# A helpers for DispatchWithEvents - this becomes __setattr__ for the
+# temporary class.
+def _event_setattr_(self, attr, val):
+ try:
+ # Does the COM object have an attribute of this name?
+ self.__class__.__bases__[0].__setattr__(self, attr, val)
+ except AttributeError:
+ # Otherwise just stash it away in the instance.
+ self.__dict__[attr] = val
+
+# An instance of this "proxy" is created to break the COM circular references
+# that exist (ie, when we connect to the COM events, COM keeps a reference
+# to the object. Thus, the Event connection must be manually broken before
+# our object can die. This solves the problem by manually breaking the connection
+# to the real object as the proxy dies.
+class EventsProxy:
+ def __init__(self, ob):
+ self.__dict__['_obj_'] = ob
+ def __del__(self):
+ try:
+ # If there is a COM error on disconnection we should
+ # just ignore it - object probably already shut down...
+ self._obj_.close()
+ except pythoncom.com_error:
+ pass
+ def __getattr__(self, attr):
+ return getattr(self._obj_, attr)
+ def __setattr__(self, attr, val):
+ setattr(self._obj_, attr, val)
+
+def DispatchWithEvents(clsid, user_event_class):
+ """Create a COM object that can fire events to a user defined class.
+ clsid -- The ProgID or CLSID of the object to create.
+ user_event_class -- A Python class object that responds to the events.
+
+ This requires makepy support for the COM object being created. If
+ this support does not exist it will be automatically generated by
+ this function. If the object does not support makepy, a TypeError
+ exception will be raised.
+
+ The result is a class instance that both represents the COM object
+ and handles events from the COM object.
+
+ It is important to note that the returned instance is not a direct
+ instance of the user_event_class, but an instance of a temporary
+ class object that derives from three classes:
+ * The makepy generated class for the COM object
+ * The makepy generated class for the COM events
+ * The user_event_class as passed to this function.
+
+ If this is not suitable, see the getevents function for an alternative
+ technique of handling events.
+
+ Object Lifetimes: Whenever the object returned from this function is
+ cleaned-up by Python, the events will be disconnected from
+ the COM object. This is almost always what should happen,
+ but see the documentation for getevents() for more details.
+
+ Example:
+
+ >>> class IEEvents:
+ ... def OnVisible(self, visible):
+ ... print "Visible changed:", visible
+ ...
+ >>> ie = DispatchWithEvents("InternetExplorer.Application", IEEvents)
+ >>> ie.Visible = 1
+ Visible changed: 1
+ >>>
+ """
+ # Create/Get the object.
+ disp = Dispatch(clsid)
+ if not disp.__class__.__dict__.get("CLSID"): # Eeek - no makepy support - try and build it.
+ try:
+ ti = disp._oleobj_.GetTypeInfo()
+ disp_clsid = ti.GetTypeAttr()[0]
+ tlb, index = ti.GetContainingTypeLib()
+ tla = tlb.GetLibAttr()
+ gencache.EnsureModule(tla[0], tla[1], tla[3], tla[4], bValidateFile=0)
+ # Get the class from the module.
+ disp_class = gencache.GetClassForProgID(str(disp_clsid))
+ except pythoncom.com_error:
+ raise TypeError("This COM object can not automate the makepy process - please run makepy manually for this object")
+ else:
+ disp_class = disp.__class__
+ # If the clsid was an object, get the clsid
+ clsid = disp_class.CLSID
+ # Create a new class that derives from 3 classes - the dispatch class, the event sink class and the user class.
+ # XXX - we are still "classic style" classes in py2x, so we need can't yet
+ # use 'type()' everywhere - revisit soon, as py2x will move to new-style too...
+ try:
+ from types import ClassType as new_type
+ except ImportError:
+ new_type = type # py3k
+ events_class = getevents(clsid)
+ if events_class is None:
+ raise ValueError("This COM object does not support events.")
+ result_class = new_type("COMEventClass", (disp_class, events_class, user_event_class), {"__setattr__" : _event_setattr_})
+ instance = result_class(disp._oleobj_) # This only calls the first base class __init__.
+ events_class.__init__(instance, instance)
+ if hasattr(user_event_class, "__init__"):
+ user_event_class.__init__(instance)
+ return EventsProxy(instance)
+
+def WithEvents(disp, user_event_class):
+ """Similar to DispatchWithEvents - except that the returned
+ object is *not* also usable as the original Dispatch object - that is
+ the returned object is not dispatchable.
+
+ The difference is best summarised by example.
+
+ >>> class IEEvents:
+ ... def OnVisible(self, visible):
+ ... print "Visible changed:", visible
+ ...
+ >>> ie = Dispatch("InternetExplorer.Application")
+ >>> ie_events = WithEvents(ie, IEEvents)
+ >>> ie.Visible = 1
+ Visible changed: 1
+
+ Compare with the code sample for DispatchWithEvents, where you get a
+ single object that is both the interface and the event handler. Note that
+ the event handler instance will *not* be able to use 'self.' to refer to
+ IE's methods and properties.
+
+ This is mainly useful where using DispatchWithEvents causes
+ circular reference problems that the simple proxy doesn't deal with
+ """
+ disp = Dispatch(disp)
+ if not disp.__class__.__dict__.get("CLSID"): # Eeek - no makepy support - try and build it.
+ try:
+ ti = disp._oleobj_.GetTypeInfo()
+ disp_clsid = ti.GetTypeAttr()[0]
+ tlb, index = ti.GetContainingTypeLib()
+ tla = tlb.GetLibAttr()
+ gencache.EnsureModule(tla[0], tla[1], tla[3], tla[4], bValidateFile=0)
+ # Get the class from the module.
+ disp_class = gencache.GetClassForProgID(str(disp_clsid))
+ except pythoncom.com_error:
+ raise TypeError("This COM object can not automate the makepy process - please run makepy manually for this object")
+ else:
+ disp_class = disp.__class__
+ # Get the clsid
+ clsid = disp_class.CLSID
+ # Create a new class that derives from 2 classes - the event sink
+ # class and the user class.
+ try:
+ from types import ClassType as new_type
+ except ImportError:
+ new_type = type # py3k
+ events_class = getevents(clsid)
+ if events_class is None:
+ raise ValueError("This COM object does not support events.")
+ result_class = new_type("COMEventClass", (events_class, user_event_class), {})
+ instance = result_class(disp) # This only calls the first base class __init__.
+ if hasattr(user_event_class, "__init__"):
+ user_event_class.__init__(instance)
+ return instance
+
+def getevents(clsid):
+ """Determine the default outgoing interface for a class, given
+ either a clsid or progid. It returns a class - you can
+ conveniently derive your own handler from this class and implement
+ the appropriate methods.
+
+ This method relies on the classes produced by makepy. You must use
+ either makepy or the gencache module to ensure that the
+ appropriate support classes have been generated for the com server
+ that you will be handling events from.
+
+ Beware of COM circular references. When the Events class is connected
+ to the COM object, the COM object itself keeps a reference to the Python
+ events class. Thus, neither the Events instance or the COM object will
+ ever die by themselves. The 'close' method on the events instance
+ must be called to break this chain and allow standard Python collection
+ rules to manage object lifetimes. Note that DispatchWithEvents() does
+ work around this problem by the use of a proxy object, but if you use
+ the getevents() function yourself, you must make your own arrangements
+ to manage this circular reference issue.
+
+ Beware of creating Python circular references: this will happen if your
+ handler has a reference to an object that has a reference back to
+ the event source. Call the 'close' method to break the chain.
+
+ Example:
+
+ >>>win32com.client.gencache.EnsureModule('{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}',0,1,1)
+ >>
+ >>> class InternetExplorerEvents(win32com.client.getevents("InternetExplorer.Application.1")):
+ ... def OnVisible(self, Visible):
+ ... print "Visibility changed: ", Visible
+ ...
+ >>>
+ >>> ie=win32com.client.Dispatch("InternetExplorer.Application.1")
+ >>> events=InternetExplorerEvents(ie)
+ >>> ie.Visible=1
+ Visibility changed: 1
+ >>>
+ """
+
+ # find clsid given progid or clsid
+ clsid=str(pywintypes.IID(clsid))
+ # return default outgoing interface for that class
+ klass = gencache.GetClassForCLSID(clsid)
+ try:
+ return klass.default_source
+ except AttributeError:
+ # See if we have a coclass for the interfaces.
+ try:
+ return gencache.GetClassForCLSID(klass.coclass_clsid).default_source
+ except AttributeError:
+ return None
+
+# A Record object, as used by the COM struct support
+def Record(name, object):
+ """Creates a new record object, given the name of the record,
+ and an object from the same type library.
+
+ Example usage would be:
+ app = win32com.client.Dispatch("Some.Application")
+ point = win32com.client.Record("SomeAppPoint", app)
+ point.x = 0
+ point.y = 0
+ app.MoveTo(point)
+ """
+ # XXX - to do - probably should allow "object" to already be a module object.
+ from . import gencache
+ object = gencache.EnsureDispatch(object)
+ module = sys.modules[object.__class__.__module__]
+ # to allow us to work correctly with "demand generated" code,
+ # we must use the typelib CLSID to obtain the module
+ # (otherwise we get the sub-module for the object, which
+ # does not hold the records)
+ # thus, package may be module, or may be module's parent if demand generated.
+ package = gencache.GetModuleForTypelib(module.CLSID, module.LCID, module.MajorVersion, module.MinorVersion)
+ try:
+ struct_guid = package.RecordMap[name]
+ except KeyError:
+ raise ValueError("The structure '%s' is not defined in module '%s'" % (name, package))
+ return pythoncom.GetRecordFromGuids(module.CLSID, module.MajorVersion, module.MinorVersion, module.LCID, struct_guid)
+
+
+############################################
+# The base of all makepy generated classes
+############################################
+class DispatchBaseClass:
+ def __init__(self, oobj=None):
+ if oobj is None:
+ oobj = pythoncom.new(self.CLSID)
+ elif isinstance(oobj, DispatchBaseClass):
+ try:
+ oobj = oobj._oleobj_.QueryInterface(self.CLSID, pythoncom.IID_IDispatch) # Must be a valid COM instance
+ except pythoncom.com_error as details:
+ import winerror
+ # Some stupid objects fail here, even tho it is _already_ IDispatch!!??
+ # Eg, Lotus notes.
+ # So just let it use the existing object if E_NOINTERFACE
+ if details.hresult != winerror.E_NOINTERFACE:
+ raise
+ oobj = oobj._oleobj_
+ self.__dict__["_oleobj_"] = oobj # so we dont call __setattr__
+ # Provide a prettier name than the CLSID
+ def __repr__(self):
+ # Need to get the docstring for the module for this class.
+ try:
+ mod_doc = sys.modules[self.__class__.__module__].__doc__
+ if mod_doc:
+ mod_name = "win32com.gen_py." + mod_doc
+ else:
+ mod_name = sys.modules[self.__class__.__module__].__name__
+ except KeyError:
+ mod_name = "win32com.gen_py.unknown"
+ return "<%s.%s instance at 0x%s>" % (mod_name, self.__class__.__name__, id(self))
+ # Delegate comparison to the oleobjs, as they know how to do identity.
+ def __eq__(self, other):
+ other = getattr(other, "_oleobj_", other)
+ return self._oleobj_ == other
+
+ def __ne__(self, other):
+ other = getattr(other, "_oleobj_", other)
+ return self._oleobj_ != other
+
+ def _ApplyTypes_(self, dispid, wFlags, retType, argTypes, user, resultCLSID, *args):
+ return self._get_good_object_(
+ self._oleobj_.InvokeTypes(dispid, 0, wFlags, retType, argTypes, *args),
+ user, resultCLSID)
+
+ def __getattr__(self, attr):
+ args=self._prop_map_get_.get(attr)
+ if args is None:
+ raise AttributeError("'%s' object has no attribute '%s'" % (repr(self), attr))
+ return self._ApplyTypes_(*args)
+
+ def __setattr__(self, attr, value):
+ if attr in self.__dict__: self.__dict__[attr] = value; return
+ try:
+ args, defArgs=self._prop_map_put_[attr]
+ except KeyError:
+ raise AttributeError("'%s' object has no attribute '%s'" % (repr(self), attr))
+ self._oleobj_.Invoke(*(args + (value,) + defArgs))
+ def _get_good_single_object_(self, obj, obUserName=None, resultCLSID=None):
+ return _get_good_single_object_(obj, obUserName, resultCLSID)
+ def _get_good_object_(self, obj, obUserName=None, resultCLSID=None):
+ return _get_good_object_(obj, obUserName, resultCLSID)
+
+# XXX - These should be consolidated with dynamic.py versions.
+def _get_good_single_object_(obj, obUserName=None, resultCLSID=None):
+ if _PyIDispatchType==type(obj):
+ return Dispatch(obj, obUserName, resultCLSID)
+ return obj
+
+def _get_good_object_(obj, obUserName=None, resultCLSID=None):
+ if obj is None:
+ return None
+ elif isinstance(obj, tuple):
+ obUserNameTuple = (obUserName,) * len(obj)
+ resultCLSIDTuple = (resultCLSID,) * len(obj)
+ return tuple(map(_get_good_object_, obj, obUserNameTuple, resultCLSIDTuple))
+ else:
+ return _get_good_single_object_(obj, obUserName, resultCLSID)
+
+class CoClassBaseClass:
+ def __init__(self, oobj=None):
+ if oobj is None: oobj = pythoncom.new(self.CLSID)
+ self.__dict__["_dispobj_"] = self.default_interface(oobj)
+ def __repr__(self):
+ return "" % (__doc__, self.__class__.__name__)
+
+ def __getattr__(self, attr):
+ d=self.__dict__["_dispobj_"]
+ if d is not None: return getattr(d, attr)
+ raise AttributeError(attr)
+ def __setattr__(self, attr, value):
+ if attr in self.__dict__: self.__dict__[attr] = value; return
+ try:
+ d=self.__dict__["_dispobj_"]
+ if d is not None:
+ d.__setattr__(attr, value)
+ return
+ except AttributeError:
+ pass
+ self.__dict__[attr] = value
+
+# A very simple VARIANT class. Only to be used with poorly-implemented COM
+# objects. If an object accepts an arg which is a simple "VARIANT", but still
+# is very pickly about the actual variant type (eg, isn't happy with a VT_I4,
+# which it would get from a Python integer), you can use this to force a
+# particular VT.
+class VARIANT(object):
+ def __init__(self, vt, value):
+ self.varianttype = vt
+ self._value = value
+
+ # 'value' is a property so when set by pythoncom it gets any magic wrapping
+ # which normally happens for result objects
+ def _get_value(self):
+ return self._value
+ def _set_value(self, newval):
+ self._value = _get_good_object_(newval)
+ def _del_value(self):
+ del self._value
+ value = property(_get_value, _set_value, _del_value)
+
+ def __repr__(self):
+ return "win32com.client.VARIANT(%r, %r)" % (self.varianttype, self._value)
diff --git a/Lib/site-packages/win32com/client/build.py b/Lib/site-packages/win32com/client/build.py
new file mode 100644
index 0000000..7287e6a
--- /dev/null
+++ b/Lib/site-packages/win32com/client/build.py
@@ -0,0 +1,630 @@
+"""Contains knowledge to build a COM object definition.
+
+This module is used by both the @dynamic@ and @makepy@ modules to build
+all knowledge of a COM object.
+
+This module contains classes which contain the actual knowledge of the object.
+This include parameter and return type information, the COM dispid and CLSID, etc.
+
+Other modules may use this information to generate .py files, use the information
+dynamically, or possibly even generate .html documentation for objects.
+"""
+
+#
+# NOTES: DispatchItem and MapEntry used by dynamic.py.
+# the rest is used by makepy.py
+#
+# OleItem, DispatchItem, MapEntry, BuildCallList() is used by makepy
+
+import sys
+import string
+from keyword import iskeyword
+
+import pythoncom
+from pywintypes import TimeType
+import winerror
+import datetime
+
+# It isn't really clear what the quoting rules are in a C/IDL string and
+# literals like a quote char and backslashes makes life a little painful to
+# always render the string perfectly - so just punt and fall-back to a repr()
+def _makeDocString(s):
+ if sys.version_info < (3,):
+ s = s.encode("mbcs")
+ return repr(s)
+
+error = "PythonCOM.Client.Build error"
+class NotSupportedException(Exception): pass # Raised when we cant support a param type.
+DropIndirection="DropIndirection"
+
+NoTranslateTypes = [
+ pythoncom.VT_BOOL, pythoncom.VT_CLSID, pythoncom.VT_CY,
+ pythoncom.VT_DATE, pythoncom.VT_DECIMAL, pythoncom.VT_EMPTY,
+ pythoncom.VT_ERROR, pythoncom.VT_FILETIME, pythoncom.VT_HRESULT,
+ pythoncom.VT_I1, pythoncom.VT_I2, pythoncom.VT_I4,
+ pythoncom.VT_I8, pythoncom.VT_INT, pythoncom.VT_NULL,
+ pythoncom.VT_R4, pythoncom.VT_R8, pythoncom.VT_NULL,
+ pythoncom.VT_STREAM,
+ pythoncom.VT_UI1, pythoncom.VT_UI2, pythoncom.VT_UI4,
+ pythoncom.VT_UI8, pythoncom.VT_UINT, pythoncom.VT_VOID,
+]
+
+NoTranslateMap = {}
+for v in NoTranslateTypes:
+ NoTranslateMap[v] = None
+
+class MapEntry:
+ "Simple holder for named attibutes - items in a map."
+ def __init__(self, desc_or_id, names=None, doc=None, resultCLSID=pythoncom.IID_NULL, resultDoc = None, hidden=0):
+ if type(desc_or_id)==type(0):
+ self.dispid = desc_or_id
+ self.desc = None
+ else:
+ self.dispid = desc_or_id[0]
+ self.desc = desc_or_id
+
+ self.names = names
+ self.doc = doc
+ self.resultCLSID = resultCLSID
+ self.resultDocumentation = resultDoc
+ self.wasProperty = 0 # Have I been transformed into a function so I can pass args?
+ self.hidden = hidden
+ def GetResultCLSID(self):
+ rc = self.resultCLSID
+ if rc == pythoncom.IID_NULL: return None
+ return rc
+ # Return a string, suitable for output - either "'{...}'" or "None"
+ def GetResultCLSIDStr(self):
+ rc = self.GetResultCLSID()
+ if rc is None: return "None"
+ return repr(str(rc)) # Convert the IID object to a string, then to a string in a string.
+
+ def GetResultName(self):
+ if self.resultDocumentation is None:
+ return None
+ return self.resultDocumentation[0]
+
+class OleItem:
+ typename = "OleItem"
+
+ def __init__(self, doc=None):
+ self.doc = doc
+ if self.doc:
+ self.python_name = MakePublicAttributeName(self.doc[0])
+ else:
+ self.python_name = None
+ self.bWritten = 0
+ self.bIsDispatch = 0
+ self.bIsSink = 0
+ self.clsid = None
+ self.co_class = None
+
+class DispatchItem(OleItem):
+ typename = "DispatchItem"
+
+ def __init__(self, typeinfo=None, attr=None, doc=None, bForUser=1):
+ OleItem.__init__(self,doc)
+ self.propMap = {}
+ self.propMapGet = {}
+ self.propMapPut = {}
+ self.mapFuncs = {}
+ self.defaultDispatchName = None
+ self.hidden = 0
+
+ if typeinfo:
+ self.Build(typeinfo, attr, bForUser)
+
+ def _propMapPutCheck_(self,key,item):
+ ins, outs, opts = self.CountInOutOptArgs(item.desc[2])
+ if ins>1: # if a Put property takes more than 1 arg:
+ if opts+1==ins or ins==item.desc[6]+1:
+ newKey = "Set" + key
+ deleteExisting = 0 # This one is still OK
+ else:
+ deleteExisting = 1 # No good to us
+ if key in self.mapFuncs or key in self.propMapGet:
+ newKey = "Set" + key
+ else:
+ newKey = key
+ item.wasProperty = 1
+ self.mapFuncs[newKey] = item
+ if deleteExisting:
+ del self.propMapPut[key]
+
+ def _propMapGetCheck_(self,key,item):
+ ins, outs, opts = self.CountInOutOptArgs(item.desc[2])
+ if ins > 0: # if a Get property takes _any_ in args:
+ if item.desc[6]==ins or ins==opts:
+ newKey = "Get" + key
+ deleteExisting = 0 # This one is still OK
+ else:
+ deleteExisting = 1 # No good to us
+ if key in self.mapFuncs:
+ newKey = "Get" + key
+ else:
+ newKey = key
+ item.wasProperty = 1
+ self.mapFuncs[newKey] = item
+ if deleteExisting:
+ del self.propMapGet[key]
+
+ def _AddFunc_(self,typeinfo,fdesc,bForUser):
+ id = fdesc.memid
+ funcflags = fdesc.wFuncFlags
+ try:
+ names = typeinfo.GetNames(id)
+ name=names[0]
+ except pythoncom.ole_error:
+ name = ""
+ names = None
+
+ doc = None
+ try:
+ if bForUser:
+ doc = typeinfo.GetDocumentation(id)
+ except pythoncom.ole_error:
+ pass
+
+ if id==0 and name:
+ self.defaultDispatchName = name
+
+ invkind = fdesc.invkind
+
+ # We need to translate any Alias', Enums, structs etc in result and args
+ typerepr, flag, defval = fdesc.rettype
+# sys.stderr.write("%s result - %s -> " % (name, typerepr))
+ typerepr, resultCLSID, resultDoc = _ResolveType(typerepr, typeinfo)
+# sys.stderr.write("%s\n" % (typerepr,))
+ fdesc.rettype = typerepr, flag, defval, resultCLSID
+ # Translate any Alias or Enums in argument list.
+ argList = []
+ for argDesc in fdesc.args:
+ typerepr, flag, defval = argDesc
+# sys.stderr.write("%s arg - %s -> " % (name, typerepr))
+ arg_type, arg_clsid, arg_doc = _ResolveType(typerepr, typeinfo)
+ argDesc = arg_type, flag, defval, arg_clsid
+# sys.stderr.write("%s\n" % (argDesc[0],))
+ argList.append(argDesc)
+ fdesc.args = tuple(argList)
+
+ hidden = (funcflags & pythoncom.FUNCFLAG_FHIDDEN) != 0
+ if invkind == pythoncom.INVOKE_PROPERTYGET:
+ map = self.propMapGet
+ # This is not the best solution, but I dont think there is
+ # one without specific "set" syntax.
+ # If there is a single PUT or PUTREF, it will function as a property.
+ # If there are both, then the PUT remains a property, and the PUTREF
+ # gets transformed into a function.
+ # (in vb, PUT=="obj=other_obj", PUTREF="set obj=other_obj
+ elif invkind in (pythoncom.INVOKE_PROPERTYPUT, pythoncom.INVOKE_PROPERTYPUTREF):
+ # Special case
+ existing = self.propMapPut.get(name, None)
+ if existing is not None:
+ if existing.desc[4]==pythoncom.INVOKE_PROPERTYPUT: # Keep this one
+ map = self.mapFuncs
+ name = "Set"+name
+ else: # Existing becomes a func.
+ existing.wasProperty = 1
+ self.mapFuncs["Set"+name]=existing
+ map = self.propMapPut # existing gets overwritten below.
+ else:
+ map = self.propMapPut # first time weve seen it.
+
+ elif invkind == pythoncom.INVOKE_FUNC:
+ map = self.mapFuncs
+ else:
+ map = None
+ if not map is None:
+# if map.has_key(name):
+# sys.stderr.write("Warning - overwriting existing method/attribute %s\n" % name)
+ map[name] = MapEntry(tuple(fdesc), names, doc, resultCLSID, resultDoc, hidden)
+ # any methods that can't be reached via DISPATCH we return None
+ # for, so dynamic dispatch doesnt see it.
+ if fdesc.funckind != pythoncom.FUNC_DISPATCH:
+ return None
+ return (name,map)
+ return None
+
+ def _AddVar_(self,typeinfo,fdesc,bForUser):
+ ### need pythoncom.VARFLAG_FRESTRICTED ...
+ ### then check it
+
+ if fdesc.varkind == pythoncom.VAR_DISPATCH:
+ id = fdesc.memid
+ names = typeinfo.GetNames(id)
+ # Translate any Alias or Enums in result.
+ typerepr, flags, defval = fdesc.elemdescVar
+ typerepr, resultCLSID, resultDoc = _ResolveType(typerepr, typeinfo)
+ fdesc.elemdescVar = typerepr, flags, defval
+ doc = None
+ try:
+ if bForUser: doc = typeinfo.GetDocumentation(id)
+ except pythoncom.ole_error:
+ pass
+
+ # handle the enumerator specially
+ map = self.propMap
+ # Check if the element is hidden.
+ hidden = 0
+ if hasattr(fdesc,"wVarFlags"):
+ hidden = (fdesc.wVarFlags & 0x40) != 0 # VARFLAG_FHIDDEN
+ map[names[0]] = MapEntry(tuple(fdesc), names, doc, resultCLSID, resultDoc, hidden)
+ return (names[0],map)
+ else:
+ return None
+
+ def Build(self, typeinfo, attr, bForUser = 1):
+ self.clsid = attr[0]
+ self.bIsDispatch = (attr.wTypeFlags & pythoncom.TYPEFLAG_FDISPATCHABLE) != 0
+ if typeinfo is None: return
+ # Loop over all methods
+ for j in range(attr[6]):
+ fdesc = typeinfo.GetFuncDesc(j)
+ self._AddFunc_(typeinfo,fdesc,bForUser)
+
+ # Loop over all variables (ie, properties)
+ for j in range(attr[7]):
+ fdesc = typeinfo.GetVarDesc(j)
+ self._AddVar_(typeinfo,fdesc,bForUser)
+
+ # Now post-process the maps. For any "Get" or "Set" properties
+ # that have arguments, we must turn them into methods. If a method
+ # of the same name already exists, change the name.
+ for key, item in list(self.propMapGet.items()):
+ self._propMapGetCheck_(key,item)
+
+ for key, item in list(self.propMapPut.items()):
+ self._propMapPutCheck_(key,item)
+
+ def CountInOutOptArgs(self, argTuple):
+ "Return tuple counting in/outs/OPTS. Sum of result may not be len(argTuple), as some args may be in/out."
+ ins = out = opts = 0
+ for argCheck in argTuple:
+ inOut = argCheck[1]
+ if inOut==0:
+ ins = ins + 1
+ out = out + 1
+ else:
+ if inOut & pythoncom.PARAMFLAG_FIN:
+ ins = ins + 1
+ if inOut & pythoncom.PARAMFLAG_FOPT:
+ opts = opts + 1
+ if inOut & pythoncom.PARAMFLAG_FOUT:
+ out = out + 1
+ return ins, out, opts
+
+ def MakeFuncMethod(self, entry, name, bMakeClass = 1):
+ # If we have a type description, and not varargs...
+ if entry.desc is not None and (len(entry.desc) < 6 or entry.desc[6]!=-1):
+ return self.MakeDispatchFuncMethod(entry, name, bMakeClass)
+ else:
+ return self.MakeVarArgsFuncMethod(entry, name, bMakeClass)
+
+ def MakeDispatchFuncMethod(self, entry, name, bMakeClass = 1):
+ fdesc = entry.desc
+ doc = entry.doc
+ names = entry.names
+ ret = []
+ if bMakeClass:
+ linePrefix = "\t"
+ defNamedOptArg = "defaultNamedOptArg"
+ defNamedNotOptArg = "defaultNamedNotOptArg"
+ defUnnamedArg = "defaultUnnamedArg"
+ else:
+ linePrefix = ""
+ defNamedOptArg = "pythoncom.Missing"
+ defNamedNotOptArg = "pythoncom.Missing"
+ defUnnamedArg = "pythoncom.Missing"
+ defOutArg = "pythoncom.Missing"
+ id = fdesc[0]
+
+ s = linePrefix + 'def ' + name + '(self' + BuildCallList(fdesc, names, defNamedOptArg, defNamedNotOptArg, defUnnamedArg, defOutArg) + '):'
+ ret.append(s)
+ if doc and doc[1]:
+ ret.append(linePrefix + '\t' + _makeDocString(doc[1]))
+
+# print "fdesc is ", fdesc
+
+ resclsid = entry.GetResultCLSID()
+ if resclsid:
+ resclsid = "'%s'" % resclsid
+ else:
+ resclsid = 'None'
+ # Strip the default values from the arg desc
+ retDesc = fdesc[8][:2]
+ argsDesc = tuple([what[:2] for what in fdesc[2]])
+ # The runtime translation of the return types is expensive, so when we know the
+ # return type of the function, there is no need to check the type at runtime.
+ # To qualify, this function must return a "simple" type, and have no byref args.
+ # Check if we have byrefs or anything in the args which mean we still need a translate.
+ param_flags = [what[1] for what in fdesc[2]]
+ bad_params = [flag for flag in param_flags if flag & (pythoncom.PARAMFLAG_FOUT | pythoncom.PARAMFLAG_FRETVAL)!=0]
+ s = None
+ if len(bad_params)==0 and len(retDesc)==2 and retDesc[1]==0:
+ rd = retDesc[0]
+ if rd in NoTranslateMap:
+ s = '%s\treturn self._oleobj_.InvokeTypes(%d, LCID, %s, %s, %s%s)' % (linePrefix, id, fdesc[4], retDesc, argsDesc, _BuildArgList(fdesc, names))
+ elif rd in [pythoncom.VT_DISPATCH, pythoncom.VT_UNKNOWN]:
+ s = '%s\tret = self._oleobj_.InvokeTypes(%d, LCID, %s, %s, %s%s)\n' % (linePrefix, id, fdesc[4], retDesc, repr(argsDesc), _BuildArgList(fdesc, names))
+ s = s + '%s\tif ret is not None:\n' % (linePrefix,)
+ if rd == pythoncom.VT_UNKNOWN:
+ s = s + "%s\t\t# See if this IUnknown is really an IDispatch\n" % (linePrefix,)
+ s = s + "%s\t\ttry:\n" % (linePrefix,)
+ s = s + "%s\t\t\tret = ret.QueryInterface(pythoncom.IID_IDispatch)\n" % (linePrefix,)
+ s = s + "%s\t\texcept pythoncom.error:\n" % (linePrefix,)
+ s = s + "%s\t\t\treturn ret\n" % (linePrefix,)
+ s = s + '%s\t\tret = Dispatch(ret, %s, %s)\n' % (linePrefix,repr(name), resclsid)
+ s = s + '%s\treturn ret' % (linePrefix)
+ elif rd == pythoncom.VT_BSTR:
+ s = "%s\t# Result is a Unicode object\n" % (linePrefix,)
+ s = s + '%s\treturn self._oleobj_.InvokeTypes(%d, LCID, %s, %s, %s%s)' % (linePrefix, id, fdesc[4], retDesc, repr(argsDesc), _BuildArgList(fdesc, names))
+ # else s remains None
+ if s is None:
+ s = '%s\treturn self._ApplyTypes_(%d, %s, %s, %s, %s, %s%s)' % (linePrefix, id, fdesc[4], retDesc, argsDesc, repr(name), resclsid, _BuildArgList(fdesc, names))
+
+ ret.append(s)
+ ret.append("")
+ return ret
+
+ def MakeVarArgsFuncMethod(self, entry, name, bMakeClass = 1):
+ fdesc = entry.desc
+ names = entry.names
+ doc = entry.doc
+ ret = []
+ argPrefix = "self"
+ if bMakeClass:
+ linePrefix = "\t"
+ else:
+ linePrefix = ""
+ ret.append(linePrefix + 'def ' + name + '(' + argPrefix + ', *args):')
+ if doc and doc[1]: ret.append(linePrefix + '\t' + _makeDocString(doc[1]))
+ if fdesc:
+ invoketype = fdesc[4]
+ else:
+ invoketype = pythoncom.DISPATCH_METHOD
+ s = linePrefix + '\treturn self._get_good_object_(self._oleobj_.Invoke(*(('
+ ret.append(s + str(entry.dispid) + ",0,%d,1)+args)),'%s')" % (invoketype, names[0]))
+ ret.append("")
+ return ret
+
+# Note - "DispatchItem" poorly named - need a new intermediate class.
+class VTableItem(DispatchItem):
+ def Build(self, typeinfo, attr, bForUser = 1):
+ DispatchItem.Build(self, typeinfo, attr, bForUser)
+ assert typeinfo is not None, "Cant build vtables without type info!"
+
+ meth_list = list(self.mapFuncs.values()) + list(self.propMapGet.values()) + list(self.propMapPut.values())
+ meth_list.sort(key=lambda m: m.desc[7])
+
+ # Now turn this list into the run-time representation
+ # (ready for immediate use or writing to gencache)
+ self.vtableFuncs = []
+ for entry in meth_list:
+ self.vtableFuncs.append( (entry.names, entry.dispid, entry.desc) )
+
+# A Lazy dispatch item - builds an item on request using info from
+# an ITypeComp. The dynamic module makes the called to build each item,
+# and also holds the references to the typeinfo and typecomp.
+class LazyDispatchItem(DispatchItem):
+ typename = "LazyDispatchItem"
+ def __init__(self, attr, doc):
+ self.clsid = attr[0]
+ DispatchItem.__init__(self, None, attr, doc, 0)
+
+typeSubstMap = {
+ pythoncom.VT_INT: pythoncom.VT_I4,
+ pythoncom.VT_UINT: pythoncom.VT_UI4,
+ pythoncom.VT_HRESULT: pythoncom.VT_I4,
+}
+
+def _ResolveType(typerepr, itypeinfo):
+ # Resolve VT_USERDEFINED (often aliases or typed IDispatches)
+
+ if type(typerepr)==tuple:
+ indir_vt, subrepr = typerepr
+ if indir_vt == pythoncom.VT_PTR:
+ # If it is a VT_PTR to a VT_USERDEFINED that is an IDispatch/IUnknown,
+ # then it resolves to simply the object.
+ # Otherwise, it becomes a ByRef of the resolved type
+ # We need to drop an indirection level on pointer to user defined interfaces.
+ # eg, (VT_PTR, (VT_USERDEFINED, somehandle)) needs to become VT_DISPATCH
+ # only when "somehandle" is an object.
+ # but (VT_PTR, (VT_USERDEFINED, otherhandle)) doesnt get the indirection dropped.
+ was_user = type(subrepr)==tuple and subrepr[0]==pythoncom.VT_USERDEFINED
+ subrepr, sub_clsid, sub_doc = _ResolveType(subrepr, itypeinfo)
+ if was_user and subrepr in [pythoncom.VT_DISPATCH, pythoncom.VT_UNKNOWN, pythoncom.VT_RECORD]:
+ # Drop the VT_PTR indirection
+ return subrepr, sub_clsid, sub_doc
+ # Change PTR indirection to byref
+ return subrepr | pythoncom.VT_BYREF, sub_clsid, sub_doc
+ if indir_vt == pythoncom.VT_SAFEARRAY:
+ # resolve the array element, and convert to VT_ARRAY
+ subrepr, sub_clsid, sub_doc = _ResolveType(subrepr, itypeinfo)
+ return pythoncom.VT_ARRAY | subrepr, sub_clsid, sub_doc
+ if indir_vt == pythoncom.VT_CARRAY: # runtime has no support for this yet.
+ # resolve the array element, and convert to VT_CARRAY
+ # sheesh - return _something_
+ return pythoncom.VT_CARRAY, None, None
+ if indir_vt == pythoncom.VT_USERDEFINED:
+ try:
+ resultTypeInfo = itypeinfo.GetRefTypeInfo(subrepr)
+ except pythoncom.com_error as details:
+ if details.hresult in [winerror.TYPE_E_CANTLOADLIBRARY, winerror.TYPE_E_LIBNOTREGISTERED]:
+ # an unregistered interface
+ return pythoncom.VT_UNKNOWN, None, None
+ raise
+
+ resultAttr = resultTypeInfo.GetTypeAttr()
+ typeKind = resultAttr.typekind
+ if typeKind == pythoncom.TKIND_ALIAS:
+ tdesc = resultAttr.tdescAlias
+ return _ResolveType(tdesc, resultTypeInfo)
+ elif typeKind in [pythoncom.TKIND_ENUM, pythoncom.TKIND_MODULE]:
+ # For now, assume Long
+ return pythoncom.VT_I4, None, None
+
+ elif typeKind == pythoncom.TKIND_DISPATCH:
+ clsid = resultTypeInfo.GetTypeAttr()[0]
+ retdoc = resultTypeInfo.GetDocumentation(-1)
+ return pythoncom.VT_DISPATCH, clsid, retdoc
+
+ elif typeKind in [pythoncom.TKIND_INTERFACE,
+ pythoncom.TKIND_COCLASS]:
+ # XXX - should probably get default interface for CO_CLASS???
+ clsid = resultTypeInfo.GetTypeAttr()[0]
+ retdoc = resultTypeInfo.GetDocumentation(-1)
+ return pythoncom.VT_UNKNOWN, clsid, retdoc
+
+ elif typeKind == pythoncom.TKIND_RECORD:
+ return pythoncom.VT_RECORD, None, None
+ raise NotSupportedException("Can not resolve alias or user-defined type")
+ return typeSubstMap.get(typerepr,typerepr), None, None
+
+def _BuildArgList(fdesc, names):
+ "Builds list of args to the underlying Invoke method."
+ # Word has TypeInfo for Insert() method, but says "no args"
+ numArgs = max(fdesc[6], len(fdesc[2]))
+ names = list(names)
+ while None in names:
+ i = names.index(None)
+ names[i] = "arg%d" % (i,)
+ # We've seen 'source safe' libraries offer the name of 'ret' params in
+ # 'names' - although we can't reproduce this, it would be insane to offer
+ # more args than we have arg infos for - hence the upper limit on names...
+ names = list(map(MakePublicAttributeName, names[1:(numArgs + 1)]))
+ name_num = 0
+ while len(names) < numArgs:
+ names.append("arg%d" % (len(names),))
+ # As per BuildCallList(), avoid huge lines.
+ # Hack a "\n" at the end of every 5th name - "strides" would be handy
+ # here but don't exist in 2.2
+ for i in range(0, len(names), 5):
+ names[i] = names[i] + "\n\t\t\t"
+ return "," + ", ".join(names)
+
+valid_identifier_chars = string.ascii_letters + string.digits + "_"
+
+def demunge_leading_underscores(className):
+ i = 0
+ while className[i] == "_":
+ i += 1
+ assert i >= 2, "Should only be here with names starting with '__'"
+ return className[i-1:] + className[:i-1]
+
+# Given a "public name" (eg, the name of a class, function, etc)
+# make sure it is a legal (and reasonable!) Python name.
+def MakePublicAttributeName(className, is_global = False):
+ # Given a class attribute that needs to be public, convert it to a
+ # reasonable name.
+ # Also need to be careful that the munging doesnt
+ # create duplicates - eg, just removing a leading "_" is likely to cause
+ # a clash.
+ # if is_global is True, then the name is a global variable that may
+ # overwrite a builtin - eg, "None"
+ if className[:2]=='__':
+ return demunge_leading_underscores(className)
+ elif className == 'None':
+ # assign to None is evil (and SyntaxError in 2.4, even though
+ # iskeyword says False there) - note that if it was a global
+ # it would get picked up below
+ className = 'NONE'
+ elif iskeyword(className):
+ # most keywords are lower case (except True, False etc in py3k)
+ ret = className.capitalize()
+ # but those which aren't get forced upper.
+ if ret == className:
+ ret = ret.upper()
+ return ret
+ elif is_global and hasattr(__builtins__, className):
+ # builtins may be mixed case. If capitalizing it doesn't change it,
+ # force to all uppercase (eg, "None", "True" become "NONE", "TRUE"
+ ret = className.capitalize()
+ if ret==className: # didn't change - force all uppercase.
+ ret = ret.upper()
+ return ret
+ # Strip non printable chars
+ return ''.join([char for char in className if char in valid_identifier_chars])
+
+# Given a default value passed by a type library, return a string with
+# an appropriate repr() for the type.
+# Takes a raw ELEMDESC and returns a repr string, or None
+# (NOTE: The string itself may be '"None"', which is valid, and different to None.
+# XXX - To do: Dates are probably screwed, but can they come in?
+def MakeDefaultArgRepr(defArgVal):
+ try:
+ inOut = defArgVal[1]
+ except IndexError:
+ # something strange - assume is in param.
+ inOut = pythoncom.PARAMFLAG_FIN
+
+ if inOut & pythoncom.PARAMFLAG_FHASDEFAULT:
+ # times need special handling...
+ val = defArgVal[2]
+ if isinstance(val, datetime.datetime):
+ # VARIANT <-> SYSTEMTIME conversions always lose any sub-second
+ # resolution, so just use a 'timetuple' here.
+ return repr(tuple(val.utctimetuple()))
+ if type(val) is TimeType:
+ # must be the 'old' pywintypes time object...
+ year=val.year; month=val.month; day=val.day; hour=val.hour; minute=val.minute; second=val.second; msec=val.msec
+ return "pywintypes.Time((%(year)d, %(month)d, %(day)d, %(hour)d, %(minute)d, %(second)d,0,0,0,%(msec)d))" % locals()
+ return repr(val)
+ return None
+
+def BuildCallList(fdesc, names, defNamedOptArg, defNamedNotOptArg, defUnnamedArg, defOutArg, is_comment = False):
+ "Builds a Python declaration for a method."
+ # Names[0] is the func name - param names are from 1.
+ numArgs = len(fdesc[2])
+ numOptArgs = fdesc[6]
+ strval = ''
+ if numOptArgs==-1: # Special value that says "var args after here"
+ firstOptArg = numArgs
+ numArgs = numArgs - 1
+ else:
+ firstOptArg = numArgs - numOptArgs
+ for arg in range(numArgs):
+ try:
+ argName = names[arg+1]
+ namedArg = argName is not None
+ except IndexError:
+ namedArg = 0
+ if not namedArg: argName = "arg%d" % (arg)
+ thisdesc = fdesc[2][arg]
+ # See if the IDL specified a default value
+ defArgVal = MakeDefaultArgRepr(thisdesc)
+ if defArgVal is None:
+ # Out params always get their special default
+ if thisdesc[1] & (pythoncom.PARAMFLAG_FOUT | pythoncom.PARAMFLAG_FIN) == pythoncom.PARAMFLAG_FOUT:
+ defArgVal = defOutArg
+ else:
+ # Unnamed arg - always allow default values.
+ if namedArg:
+ # Is a named argument
+ if arg >= firstOptArg:
+ defArgVal = defNamedOptArg
+ else:
+ defArgVal = defNamedNotOptArg
+ else:
+ defArgVal = defUnnamedArg
+
+ argName = MakePublicAttributeName(argName)
+ # insanely long lines with an 'encoding' flag crashes python 2.4.0
+ # keep 5 args per line
+ # This may still fail if the arg names are insane, but that seems
+ # unlikely. See also _BuildArgList()
+ if (arg+1) % 5 == 0:
+ strval = strval + "\n"
+ if is_comment:
+ strval = strval + "#"
+ strval = strval + "\t\t\t"
+ strval = strval + ", " + argName
+ if defArgVal:
+ strval = strval + "=" + defArgVal
+ if numOptArgs==-1:
+ strval = strval + ", *" + names[-1]
+
+ return strval
+
+
+if __name__=='__main__':
+ print("Use 'makepy.py' to generate Python code - this module is just a helper")
diff --git a/Lib/site-packages/win32com/client/combrowse.py b/Lib/site-packages/win32com/client/combrowse.py
new file mode 100644
index 0000000..1e73631
--- /dev/null
+++ b/Lib/site-packages/win32com/client/combrowse.py
@@ -0,0 +1,540 @@
+"""A utility for browsing COM objects.
+
+ Usage:
+
+ Command Prompt
+
+ Use the command *"python.exe catbrowse.py"*. This will display
+ display a fairly small, modal dialog.
+
+ Pythonwin
+
+ Use the "Run Script" menu item, and this will create the browser in an
+ MDI window. This window can be fully resized.
+
+ Details
+
+ This module allows browsing of registered Type Libraries, COM categories,
+ and running COM objects. The display is similar to the Pythonwin object
+ browser, and displays the objects in a hierarchical window.
+
+ Note that this module requires the win32ui (ie, Pythonwin) diestribution to
+ work.
+
+"""
+import win32con
+import win32api, win32ui
+import sys
+import pythoncom
+from win32com.client import util
+from pywin.tools import browser
+
+class HLIRoot(browser.HLIPythonObject):
+ def __init__(self, title):
+ self.name = title
+ def GetSubList(self):
+ return [HLIHeadingCategory(), HLI_IEnumMoniker(pythoncom.GetRunningObjectTable().EnumRunning(), "Running Objects"), HLIHeadingRegisterdTypeLibs()]
+ def __cmp__(self, other):
+ return cmp(self.name, other.name)
+
+class HLICOM(browser.HLIPythonObject):
+ def GetText(self):
+ return self.name
+ def CalculateIsExpandable(self):
+ return 1
+
+class HLICLSID(HLICOM):
+ def __init__(self, myobject, name=None ):
+ if type(myobject)==type(''):
+ myobject = pythoncom.MakeIID(myobject)
+ if name is None:
+ try:
+ name = pythoncom.ProgIDFromCLSID(myobject)
+ except pythoncom.com_error:
+ name = str(myobject)
+ name = "IID: " + name
+ HLICOM.__init__(self, myobject, name)
+ def CalculateIsExpandable(self):
+ return 0
+ def GetSubList(self):
+ return []
+
+class HLI_Interface(HLICOM):
+ pass
+
+class HLI_Enum(HLI_Interface):
+ def GetBitmapColumn(self):
+ return 0 # Always a folder.
+ def CalculateIsExpandable(self):
+ if self.myobject is not None:
+ rc = len(self.myobject.Next(1))>0
+ self.myobject.Reset()
+ else:
+ rc = 0
+ return rc
+ pass
+
+class HLI_IEnumMoniker(HLI_Enum):
+ def GetSubList(self):
+ ctx = pythoncom.CreateBindCtx()
+ ret = []
+ for mon in util.Enumerator(self.myobject):
+ ret.append(HLI_IMoniker(mon, mon.GetDisplayName(ctx, None)))
+ return ret
+
+class HLI_IMoniker(HLI_Interface):
+ def GetSubList(self):
+ ret = []
+ ret.append(browser.MakeHLI(self.myobject.Hash(), "Hash Value"))
+ subenum = self.myobject.Enum(1)
+ ret.append(HLI_IEnumMoniker(subenum, "Sub Monikers"))
+ return ret
+
+class HLIHeadingCategory(HLICOM):
+ "A tree heading for registered categories"
+ def GetText(self):
+ return "Registered Categories"
+ def GetSubList(self):
+ catinf=pythoncom.CoCreateInstance(pythoncom.CLSID_StdComponentCategoriesMgr,None,pythoncom.CLSCTX_INPROC,pythoncom.IID_ICatInformation)
+ enum=util.Enumerator(catinf.EnumCategories())
+ ret = []
+ try:
+ for catid, lcid, desc in enum:
+ ret.append(HLICategory((catid, lcid, desc)))
+ except pythoncom.com_error:
+ # Registered categories occasionally seem to give spurious errors.
+ pass # Use what we already have.
+ return ret
+
+class HLICategory(HLICOM):
+ "An actual Registered Category"
+ def GetText(self):
+ desc = self.myobject[2]
+ if not desc: desc = "(unnamed category)"
+ return desc
+ def GetSubList(self):
+ win32ui.DoWaitCursor(1)
+ catid, lcid, desc = self.myobject
+ catinf=pythoncom.CoCreateInstance(pythoncom.CLSID_StdComponentCategoriesMgr,None,pythoncom.CLSCTX_INPROC,pythoncom.IID_ICatInformation)
+ ret = []
+ for clsid in util.Enumerator(catinf.EnumClassesOfCategories((catid,),())):
+ ret.append(HLICLSID(clsid))
+ win32ui.DoWaitCursor(0)
+
+ return ret
+
+class HLIHelpFile(HLICOM):
+ def CalculateIsExpandable(self):
+ return 0
+ def GetText(self):
+ import os
+ fname, ctx = self.myobject
+ base = os.path.split(fname)[1]
+ return "Help reference in %s" %( base)
+
+ def TakeDefaultAction(self):
+ fname, ctx = self.myobject
+ if ctx:
+ cmd = win32con.HELP_CONTEXT
+ else:
+ cmd = win32con.HELP_FINDER
+ win32api.WinHelp(win32ui.GetMainFrame().GetSafeHwnd(), fname, cmd, ctx)
+ def GetBitmapColumn(self):
+ return 6
+
+class HLIRegisteredTypeLibrary(HLICOM):
+ def GetSubList(self):
+ import os
+ clsidstr, versionStr = self.myobject
+ collected = []
+ helpPath = ""
+ key = win32api.RegOpenKey(win32con.HKEY_CLASSES_ROOT, "TypeLib\\%s\\%s" % (clsidstr, versionStr))
+ win32ui.DoWaitCursor(1)
+ try:
+ num = 0
+ while 1:
+ try:
+ subKey = win32api.RegEnumKey(key, num)
+ except win32api.error:
+ break
+ hSubKey = win32api.RegOpenKey(key, subKey)
+ try:
+ value, typ = win32api.RegQueryValueEx(hSubKey, None)
+ if typ == win32con.REG_EXPAND_SZ:
+ value = win32api.ExpandEnvironmentStrings(value)
+ except win32api.error:
+ value = ""
+ if subKey=="HELPDIR":
+ helpPath = value
+ elif subKey=="Flags":
+ flags = value
+ else:
+ try:
+ lcid = int(subKey)
+ lcidkey = win32api.RegOpenKey(key, subKey)
+ # Enumerate the platforms
+ lcidnum = 0
+ while 1:
+ try:
+ platform = win32api.RegEnumKey(lcidkey, lcidnum)
+ except win32api.error:
+ break
+ try:
+ hplatform = win32api.RegOpenKey(lcidkey, platform)
+ fname, typ = win32api.RegQueryValueEx(hplatform, None)
+ if typ == win32con.REG_EXPAND_SZ:
+ fname = win32api.ExpandEnvironmentStrings(fname)
+ except win32api.error:
+ fname = ""
+ collected.append((lcid, platform, fname))
+ lcidnum = lcidnum + 1
+ win32api.RegCloseKey(lcidkey)
+ except ValueError:
+ pass
+ num = num + 1
+ finally:
+ win32ui.DoWaitCursor(0)
+ win32api.RegCloseKey(key)
+ # Now, loop over my collected objects, adding a TypeLib and a HelpFile
+ ret = []
+# if helpPath: ret.append(browser.MakeHLI(helpPath, "Help Path"))
+ ret.append(HLICLSID(clsidstr))
+ for lcid, platform, fname in collected:
+ extraDescs = []
+ if platform!="win32":
+ extraDescs.append(platform)
+ if lcid:
+ extraDescs.append("locale=%s"%lcid)
+ extraDesc = ""
+ if extraDescs: extraDesc = " (%s)" % ", ".join(extraDescs)
+ ret.append(HLITypeLib(fname, "Type Library" + extraDesc))
+ ret.sort()
+ return ret
+
+class HLITypeLibEntry(HLICOM):
+ def GetText(self):
+ tlb, index = self.myobject
+ name, doc, ctx, helpFile = tlb.GetDocumentation(index)
+ try:
+ typedesc = HLITypeKinds[tlb.GetTypeInfoType(index)][1]
+ except KeyError:
+ typedesc = "Unknown!"
+ return name + " - " + typedesc
+ def GetSubList(self):
+ tlb, index = self.myobject
+ name, doc, ctx, helpFile = tlb.GetDocumentation(index)
+ ret = []
+ if doc: ret.append(browser.HLIDocString(doc, "Doc"))
+ if helpFile: ret.append(HLIHelpFile( (helpFile, ctx) ))
+ return ret
+
+class HLICoClass(HLITypeLibEntry):
+ def GetSubList(self):
+ ret = HLITypeLibEntry.GetSubList(self)
+ tlb, index = self.myobject
+ typeinfo = tlb.GetTypeInfo(index)
+ attr = typeinfo.GetTypeAttr()
+ for j in range(attr[8]):
+ flags = typeinfo.GetImplTypeFlags(j)
+ refType = typeinfo.GetRefTypeInfo(typeinfo.GetRefTypeOfImplType(j))
+ refAttr = refType.GetTypeAttr()
+ ret.append(browser.MakeHLI(refAttr[0], "Name=%s, Flags = %d" % (refAttr[0], flags)))
+ return ret
+
+
+class HLITypeLibMethod(HLITypeLibEntry):
+ def __init__(self, ob, name = None):
+ self.entry_type = "Method"
+ HLITypeLibEntry.__init__(self, ob, name)
+ def GetSubList(self):
+ ret = HLITypeLibEntry.GetSubList(self)
+ tlb, index = self.myobject
+ typeinfo = tlb.GetTypeInfo(index)
+ attr = typeinfo.GetTypeAttr()
+ for i in range(attr[7]):
+ ret.append(HLITypeLibProperty((typeinfo, i)))
+ for i in range(attr[6]):
+ ret.append(HLITypeLibFunction((typeinfo, i)))
+ return ret
+
+class HLITypeLibEnum(HLITypeLibEntry):
+ def __init__(self, myitem):
+ typelib, index = myitem
+ typeinfo = typelib.GetTypeInfo(index)
+ self.id = typeinfo.GetVarDesc(index)[0]
+ name = typeinfo.GetNames(self.id)[0]
+ HLITypeLibEntry.__init__(self, myitem, name)
+ def GetText(self):
+ return self.name + " - Enum/Module"
+ def GetSubList(self):
+ ret = []
+ typelib, index = self.myobject
+ typeinfo = typelib.GetTypeInfo(index)
+ attr = typeinfo.GetTypeAttr()
+ for j in range(attr[7]):
+ vdesc = typeinfo.GetVarDesc(j)
+ name = typeinfo.GetNames(vdesc[0])[0]
+ ret.append(browser.MakeHLI(vdesc[1], name))
+ return ret
+
+class HLITypeLibProperty(HLICOM):
+ def __init__(self, myitem):
+ typeinfo, index = myitem
+ self.id = typeinfo.GetVarDesc(index)[0]
+ name = typeinfo.GetNames(self.id)[0]
+ HLICOM.__init__(self, myitem, name)
+ def GetText(self):
+ return self.name + " - Property"
+ def GetSubList(self):
+ ret = []
+ typeinfo, index = self.myobject
+ names = typeinfo.GetNames(self.id)
+ if len(names)>1:
+ ret.append(browser.MakeHLI(names[1:], "Named Params"))
+ vd = typeinfo.GetVarDesc(index)
+ ret.append(browser.MakeHLI(self.id, "Dispatch ID"))
+ ret.append(browser.MakeHLI(vd[1], "Value"))
+ ret.append(browser.MakeHLI(vd[2], "Elem Desc"))
+ ret.append(browser.MakeHLI(vd[3], "Var Flags"))
+ ret.append(browser.MakeHLI(vd[4], "Var Kind"))
+ return ret
+
+class HLITypeLibFunction(HLICOM):
+ funckinds = {pythoncom.FUNC_VIRTUAL : "Virtual",
+ pythoncom.FUNC_PUREVIRTUAL : "Pure Virtual",
+ pythoncom.FUNC_STATIC : "Static",
+ pythoncom.FUNC_DISPATCH : "Dispatch",
+ }
+ invokekinds = {pythoncom.INVOKE_FUNC: "Function",
+ pythoncom.INVOKE_PROPERTYGET : "Property Get",
+ pythoncom.INVOKE_PROPERTYPUT : "Property Put",
+ pythoncom.INVOKE_PROPERTYPUTREF : "Property Put by reference",
+ }
+ funcflags = [(pythoncom.FUNCFLAG_FRESTRICTED, "Restricted"),
+ (pythoncom.FUNCFLAG_FSOURCE, "Source"),
+ (pythoncom.FUNCFLAG_FBINDABLE, "Bindable"),
+ (pythoncom.FUNCFLAG_FREQUESTEDIT, "Request Edit"),
+ (pythoncom.FUNCFLAG_FDISPLAYBIND, "Display Bind"),
+ (pythoncom.FUNCFLAG_FDEFAULTBIND, "Default Bind"),
+ (pythoncom.FUNCFLAG_FHIDDEN, "Hidden"),
+ (pythoncom.FUNCFLAG_FUSESGETLASTERROR, "Uses GetLastError"),
+ ]
+
+ vartypes = {pythoncom.VT_EMPTY: "Empty",
+ pythoncom.VT_NULL: "NULL",
+ pythoncom.VT_I2: "Integer 2",
+ pythoncom.VT_I4: "Integer 4",
+ pythoncom.VT_R4: "Real 4",
+ pythoncom.VT_R8: "Real 8",
+ pythoncom.VT_CY: "CY",
+ pythoncom.VT_DATE: "Date",
+ pythoncom.VT_BSTR: "String",
+ pythoncom.VT_DISPATCH: "IDispatch",
+ pythoncom.VT_ERROR: "Error",
+ pythoncom.VT_BOOL: "BOOL",
+ pythoncom.VT_VARIANT: "Variant",
+ pythoncom.VT_UNKNOWN: "IUnknown",
+ pythoncom.VT_DECIMAL: "Decimal",
+ pythoncom.VT_I1: "Integer 1",
+ pythoncom.VT_UI1: "Unsigned integer 1",
+ pythoncom.VT_UI2: "Unsigned integer 2",
+ pythoncom.VT_UI4: "Unsigned integer 4",
+ pythoncom.VT_I8: "Integer 8",
+ pythoncom.VT_UI8: "Unsigned integer 8",
+ pythoncom.VT_INT: "Integer",
+ pythoncom.VT_UINT: "Unsigned integer",
+ pythoncom.VT_VOID: "Void",
+ pythoncom.VT_HRESULT: "HRESULT",
+ pythoncom.VT_PTR: "Pointer",
+ pythoncom.VT_SAFEARRAY: "SafeArray",
+ pythoncom.VT_CARRAY: "C Array",
+ pythoncom.VT_USERDEFINED: "User Defined",
+ pythoncom.VT_LPSTR: "Pointer to string",
+ pythoncom.VT_LPWSTR: "Pointer to Wide String",
+ pythoncom.VT_FILETIME: "File time",
+ pythoncom.VT_BLOB: "Blob",
+ pythoncom.VT_STREAM: "IStream",
+ pythoncom.VT_STORAGE: "IStorage",
+ pythoncom.VT_STORED_OBJECT: "Stored object",
+ pythoncom.VT_STREAMED_OBJECT: "Streamed object",
+ pythoncom.VT_BLOB_OBJECT: "Blob object",
+ pythoncom.VT_CF: "CF",
+ pythoncom.VT_CLSID: "CLSID",
+ }
+
+ type_flags = [ (pythoncom.VT_VECTOR, "Vector"),
+ (pythoncom.VT_ARRAY, "Array"),
+ (pythoncom.VT_BYREF, "ByRef"),
+ (pythoncom.VT_RESERVED, "Reserved"),
+ ]
+
+ def __init__(self, myitem):
+ typeinfo, index = myitem
+ self.id = typeinfo.GetFuncDesc(index)[0]
+ name = typeinfo.GetNames(self.id)[0]
+ HLICOM.__init__(self, myitem, name)
+ def GetText(self):
+ return self.name + " - Function"
+ def MakeReturnTypeName(self, typ):
+ justtyp = typ & pythoncom.VT_TYPEMASK
+ try:
+ typname = self.vartypes[justtyp]
+ except KeyError:
+ typname = "?Bad type?"
+ for (flag, desc) in self.type_flags:
+ if flag & typ:
+ typname = "%s(%s)" % (desc, typname)
+ return typname
+ def MakeReturnType(self, returnTypeDesc):
+ if type(returnTypeDesc)==type(()):
+ first = returnTypeDesc[0]
+ result = self.MakeReturnType(first)
+ if first != pythoncom.VT_USERDEFINED:
+ result = result + " " + self.MakeReturnType(returnTypeDesc[1])
+ return result
+ else:
+ return self.MakeReturnTypeName(returnTypeDesc)
+
+ def GetSubList(self):
+ ret = []
+ typeinfo, index = self.myobject
+ names = typeinfo.GetNames(self.id)
+ ret.append(browser.MakeHLI(self.id, "Dispatch ID"))
+ if len(names)>1:
+ ret.append(browser.MakeHLI(", ".join(names[1:]), "Named Params"))
+ fd = typeinfo.GetFuncDesc(index)
+ if fd[1]:
+ ret.append(browser.MakeHLI(fd[1], "Possible result values"))
+ if fd[8]:
+ typ, flags, default = fd[8]
+ val = self.MakeReturnType(typ)
+ if flags:
+ val = "%s (Flags=%d, default=%s)" % (val, flags, default)
+ ret.append(browser.MakeHLI(val, "Return Type"))
+
+ for argDesc in fd[2]:
+ typ, flags, default = argDesc
+ val = self.MakeReturnType(typ)
+ if flags:
+ val = "%s (Flags=%d)" % (val, flags)
+ if default is not None:
+ val = "%s (Default=%s)" % (val, default)
+ ret.append(browser.MakeHLI(val, "Argument"))
+
+ try:
+ fkind = self.funckinds[fd[3]]
+ except KeyError:
+ fkind = "Unknown"
+ ret.append(browser.MakeHLI(fkind, "Function Kind"))
+ try:
+ ikind = self.invokekinds[fd[4]]
+ except KeyError:
+ ikind = "Unknown"
+ ret.append(browser.MakeHLI(ikind, "Invoke Kind"))
+ # 5 = call conv
+ # 5 = offset vtbl
+ ret.append(browser.MakeHLI(fd[6], "Number Optional Params"))
+ flagDescs = []
+ for flag, desc in self.funcflags:
+ if flag & fd[9]:
+ flagDescs.append(desc)
+ if flagDescs:
+ ret.append(browser.MakeHLI(", ".join(flagDescs), "Function Flags"))
+ return ret
+
+HLITypeKinds = {
+ pythoncom.TKIND_ENUM : (HLITypeLibEnum, 'Enumeration'),
+ pythoncom.TKIND_RECORD : (HLITypeLibEntry, 'Record'),
+ pythoncom.TKIND_MODULE : (HLITypeLibEnum, 'Module'),
+ pythoncom.TKIND_INTERFACE : (HLITypeLibMethod, 'Interface'),
+ pythoncom.TKIND_DISPATCH : (HLITypeLibMethod, 'Dispatch'),
+ pythoncom.TKIND_COCLASS : (HLICoClass, 'CoClass'),
+ pythoncom.TKIND_ALIAS : (HLITypeLibEntry, 'Alias'),
+ pythoncom.TKIND_UNION : (HLITypeLibEntry, 'Union')
+ }
+
+class HLITypeLib(HLICOM):
+ def GetSubList(self):
+ ret = []
+ ret.append(browser.MakeHLI(self.myobject, "Filename"))
+ try:
+ tlb = pythoncom.LoadTypeLib(self.myobject)
+ except pythoncom.com_error:
+ return [browser.MakeHLI("%s can not be loaded" % self.myobject)]
+
+ for i in range(tlb.GetTypeInfoCount()):
+ try:
+ ret.append(HLITypeKinds[tlb.GetTypeInfoType(i)][0]( (tlb, i) ) )
+ except pythoncom.com_error:
+ ret.append(browser.MakeHLI("The type info can not be loaded!"))
+ ret.sort()
+ return ret
+
+class HLIHeadingRegisterdTypeLibs(HLICOM):
+ "A tree heading for registered type libraries"
+ def GetText(self):
+ return "Registered Type Libraries"
+ def GetSubList(self):
+ # Explicit lookup in the registry.
+ ret = []
+ key = win32api.RegOpenKey(win32con.HKEY_CLASSES_ROOT, "TypeLib")
+ win32ui.DoWaitCursor(1)
+ try:
+ num = 0
+ while 1:
+ try:
+ keyName = win32api.RegEnumKey(key, num)
+ except win32api.error:
+ break
+ # Enumerate all version info
+ subKey = win32api.RegOpenKey(key, keyName)
+ name = None
+ try:
+ subNum = 0
+ bestVersion = 0.0
+ while 1:
+ try:
+ versionStr = win32api.RegEnumKey(subKey, subNum)
+ except win32api.error:
+ break
+ try:
+ versionFlt = float(versionStr)
+ except ValueError:
+ versionFlt = 0 # ????
+ if versionFlt > bestVersion:
+ bestVersion = versionFlt
+ name = win32api.RegQueryValue(subKey, versionStr)
+ subNum = subNum + 1
+ finally:
+ win32api.RegCloseKey(subKey)
+ if name is not None:
+ ret.append(HLIRegisteredTypeLibrary((keyName, versionStr), name))
+ num = num + 1
+ finally:
+ win32api.RegCloseKey(key)
+ win32ui.DoWaitCursor(0)
+ ret.sort()
+ return ret
+
+def main():
+ from pywin.tools import hierlist
+ root = HLIRoot("COM Browser")
+ if "app" in sys.modules:
+ # do it in a window
+ browser.MakeTemplate()
+ browser.template.OpenObject(root)
+ else:
+# list=hierlist.HierListWithItems( root, win32ui.IDB_BROWSER_HIER )
+# dlg=hierlist.HierDialog("COM Browser",list)
+ dlg = browser.dynamic_browser(root)
+ dlg.DoModal()
+
+
+
+if __name__=='__main__':
+ main()
+
+ ni = pythoncom._GetInterfaceCount()
+ ng = pythoncom._GetGatewayCount()
+ if ni or ng:
+ print("Warning - exiting with %d/%d objects alive" % (ni,ng))
diff --git a/Lib/site-packages/win32com/client/connect.py b/Lib/site-packages/win32com/client/connect.py
new file mode 100644
index 0000000..4c43caf
--- /dev/null
+++ b/Lib/site-packages/win32com/client/connect.py
@@ -0,0 +1,43 @@
+"""Utilities for working with Connections"""
+import win32com.server.util, pythoncom
+
+class SimpleConnection:
+ "A simple, single connection object"
+ def __init__(self, coInstance = None, eventInstance = None, eventCLSID = None, debug = 0):
+ self.cp = None
+ self.cookie = None
+ self.debug = debug
+ if not coInstance is None:
+ self.Connect(coInstance , eventInstance, eventCLSID)
+
+ def __del__(self):
+ try:
+ self.Disconnect()
+ except pythoncom.error:
+ # Ignore disconnection as we are torn down.
+ pass
+
+ def _wrap(self, obj):
+ useDispatcher = None
+ if self.debug:
+ from win32com.server import dispatcher
+ useDispatcher = dispatcher.DefaultDebugDispatcher
+ return win32com.server.util.wrap(obj, useDispatcher=useDispatcher)
+
+ def Connect(self, coInstance, eventInstance, eventCLSID = None):
+ try:
+ oleobj = coInstance._oleobj_
+ except AttributeError:
+ oleobj = coInstance
+ cpc=oleobj.QueryInterface(pythoncom.IID_IConnectionPointContainer)
+ if eventCLSID is None: eventCLSID = eventInstance.CLSID
+ comEventInstance = self._wrap(eventInstance)
+ self.cp=cpc.FindConnectionPoint(eventCLSID)
+ self.cookie = self.cp.Advise(comEventInstance)
+
+ def Disconnect(self):
+ if not self.cp is None:
+ if self.cookie:
+ self.cp.Unadvise(self.cookie)
+ self.cookie = None
+ self.cp = None
diff --git a/Lib/site-packages/win32com/client/dynamic.py b/Lib/site-packages/win32com/client/dynamic.py
new file mode 100644
index 0000000..e2bfa1e
--- /dev/null
+++ b/Lib/site-packages/win32com/client/dynamic.py
@@ -0,0 +1,581 @@
+"""Support for dynamic COM client support.
+
+Introduction
+ Dynamic COM client support is the ability to use a COM server without
+ prior knowledge of the server. This can be used to talk to almost all
+ COM servers, including much of MS Office.
+
+ In general, you should not use this module directly - see below.
+
+Example
+ >>> import win32com.client
+ >>> xl = win32com.client.Dispatch("Excel.Application")
+ # The line above invokes the functionality of this class.
+ # xl is now an object we can use to talk to Excel.
+ >>> xl.Visible = 1 # The Excel window becomes visible.
+
+"""
+import sys
+import traceback
+import types
+
+import pythoncom
+import winerror
+from . import build
+
+from pywintypes import IIDType
+
+import win32com.client # Needed as code we eval() references it.
+
+debugging=0 # General debugging
+debugging_attr=0 # Debugging dynamic attribute lookups.
+
+LCID = 0x0
+
+# These errors generally mean the property or method exists,
+# but can't be used in this context - eg, property instead of a method, etc.
+# Used to determine if we have a real error or not.
+ERRORS_BAD_CONTEXT = [
+ winerror.DISP_E_MEMBERNOTFOUND,
+ winerror.DISP_E_BADPARAMCOUNT,
+ winerror.DISP_E_PARAMNOTOPTIONAL,
+ winerror.DISP_E_TYPEMISMATCH,
+ winerror.E_INVALIDARG,
+]
+
+ALL_INVOKE_TYPES = [
+ pythoncom.INVOKE_PROPERTYGET,
+ pythoncom.INVOKE_PROPERTYPUT,
+ pythoncom.INVOKE_PROPERTYPUTREF,
+ pythoncom.INVOKE_FUNC
+]
+
+def debug_print(*args):
+ if debugging:
+ for arg in args:
+ print(arg, end=' ')
+ print()
+
+def debug_attr_print(*args):
+ if debugging_attr:
+ for arg in args:
+ print(arg, end=' ')
+ print()
+
+# A helper to create method objects on the fly
+py3k = sys.version_info > (3,0)
+if py3k:
+ def MakeMethod(func, inst, cls):
+ return types.MethodType(func, inst) # class not needed in py3k
+else:
+ MakeMethod = types.MethodType # all args used in py2k.
+
+# get the type objects for IDispatch and IUnknown
+PyIDispatchType = pythoncom.TypeIIDs[pythoncom.IID_IDispatch]
+PyIUnknownType = pythoncom.TypeIIDs[pythoncom.IID_IUnknown]
+
+if py3k:
+ _GoodDispatchTypes=(str, IIDType)
+else:
+ _GoodDispatchTypes=(str, IIDType, str)
+_defaultDispatchItem=build.DispatchItem
+
+def _GetGoodDispatch(IDispatch, clsctx = pythoncom.CLSCTX_SERVER):
+ # quick return for most common case
+ if isinstance(IDispatch, PyIDispatchType):
+ return IDispatch
+ if isinstance(IDispatch, _GoodDispatchTypes):
+ try:
+ IDispatch = pythoncom.connect(IDispatch)
+ except pythoncom.ole_error:
+ IDispatch = pythoncom.CoCreateInstance(IDispatch, None, clsctx, pythoncom.IID_IDispatch)
+ else:
+ # may already be a wrapped class.
+ IDispatch = getattr(IDispatch, "_oleobj_", IDispatch)
+ return IDispatch
+
+def _GetGoodDispatchAndUserName(IDispatch, userName, clsctx):
+ # Get a dispatch object, and a 'user name' (ie, the name as
+ # displayed to the user in repr() etc.
+ if userName is None:
+ # Displayed name should be a plain string in py2k, and unicode in py3k
+ if isinstance(IDispatch, str):
+ userName = IDispatch
+ elif not py3k and isinstance(IDispatch, str):
+ # 2to3 converts the above 'unicode' to 'str', but this will never be executed in py3k
+ userName = IDispatch.encode("ascii", "replace")
+ ## ??? else userName remains None ???
+ elif not py3k and isinstance(userName, str):
+ # 2to3 converts the above 'unicode' to 'str', but this will never be executed in py3k
+ # As above - always a plain string in py2k
+ userName = userName.encode("ascii", "replace")
+ else:
+ userName = str(userName)
+ return (_GetGoodDispatch(IDispatch, clsctx), userName)
+
+def _GetDescInvokeType(entry, invoke_type):
+ # determine the wFlags argument passed as input to IDispatch::Invoke
+ if not entry or not entry.desc: return invoke_type
+ varkind = entry.desc[4] # from VARDESC struct returned by ITypeComp::Bind
+ if varkind == pythoncom.VAR_DISPATCH and invoke_type == pythoncom.INVOKE_PROPERTYGET:
+ return pythoncom.INVOKE_FUNC | invoke_type # DISPATCH_METHOD & DISPATCH_PROPERTYGET can be combined in IDispatch::Invoke
+ else:
+ return invoke_type
+
+def Dispatch(IDispatch, userName = None, createClass = None, typeinfo = None, UnicodeToString=None, clsctx = pythoncom.CLSCTX_SERVER):
+ assert UnicodeToString is None, "this is deprecated and will go away"
+ IDispatch, userName = _GetGoodDispatchAndUserName(IDispatch,userName,clsctx)
+ if createClass is None:
+ createClass = CDispatch
+ lazydata = None
+ try:
+ if typeinfo is None:
+ typeinfo = IDispatch.GetTypeInfo()
+ if typeinfo is not None:
+ try:
+ #try for a typecomp
+ typecomp = typeinfo.GetTypeComp()
+ lazydata = typeinfo, typecomp
+ except pythoncom.com_error:
+ pass
+ except pythoncom.com_error:
+ typeinfo = None
+ olerepr = MakeOleRepr(IDispatch, typeinfo, lazydata)
+ return createClass(IDispatch, olerepr, userName, lazydata=lazydata)
+
+def MakeOleRepr(IDispatch, typeinfo, typecomp):
+ olerepr = None
+ if typeinfo is not None:
+ try:
+ attr = typeinfo.GetTypeAttr()
+ # If the type info is a special DUAL interface, magically turn it into
+ # a DISPATCH typeinfo.
+ if attr[5] == pythoncom.TKIND_INTERFACE and attr[11] & pythoncom.TYPEFLAG_FDUAL:
+ # Get corresponding Disp interface;
+ # -1 is a special value which does this for us.
+ href = typeinfo.GetRefTypeOfImplType(-1);
+ typeinfo = typeinfo.GetRefTypeInfo(href)
+ attr = typeinfo.GetTypeAttr()
+ if typecomp is None:
+ olerepr = build.DispatchItem(typeinfo, attr, None, 0)
+ else:
+ olerepr = build.LazyDispatchItem(attr, None)
+ except pythoncom.ole_error:
+ pass
+ if olerepr is None: olerepr = build.DispatchItem()
+ return olerepr
+
+def DumbDispatch(IDispatch, userName = None, createClass = None,UnicodeToString=None, clsctx=pythoncom.CLSCTX_SERVER):
+ "Dispatch with no type info"
+ assert UnicodeToString is None, "this is deprecated and will go away"
+ IDispatch, userName = _GetGoodDispatchAndUserName(IDispatch,userName,clsctx)
+ if createClass is None:
+ createClass = CDispatch
+ return createClass(IDispatch, build.DispatchItem(), userName)
+
+class CDispatch:
+ def __init__(self, IDispatch, olerepr, userName=None, UnicodeToString=None, lazydata=None):
+ assert UnicodeToString is None, "this is deprecated and will go away"
+ if userName is None: userName = ""
+ self.__dict__['_oleobj_'] = IDispatch
+ self.__dict__['_username_'] = userName
+ self.__dict__['_olerepr_'] = olerepr
+ self.__dict__['_mapCachedItems_'] = {}
+ self.__dict__['_builtMethods_'] = {}
+ self.__dict__['_enum_'] = None
+ self.__dict__['_unicode_to_string_'] = None
+ self.__dict__['_lazydata_'] = lazydata
+
+ def __call__(self, *args):
+ "Provide 'default dispatch' COM functionality - allow instance to be called"
+ if self._olerepr_.defaultDispatchName:
+ invkind, dispid = self._find_dispatch_type_(self._olerepr_.defaultDispatchName)
+ else:
+ invkind, dispid = pythoncom.DISPATCH_METHOD | pythoncom.DISPATCH_PROPERTYGET, pythoncom.DISPID_VALUE
+ if invkind is not None:
+ allArgs = (dispid,LCID,invkind,1) + args
+ return self._get_good_object_(self._oleobj_.Invoke(*allArgs),self._olerepr_.defaultDispatchName,None)
+ raise TypeError("This dispatch object does not define a default method")
+
+ def __bool__(self):
+ return True # ie "if object:" should always be "true" - without this, __len__ is tried.
+ # _Possibly_ want to defer to __len__ if available, but Im not sure this is
+ # desirable???
+
+ def __repr__(self):
+ return "" % (self._username_)
+
+ def __str__(self):
+ # __str__ is used when the user does "print object", so we gracefully
+ # fall back to the __repr__ if the object has no default method.
+ try:
+ return str(self.__call__())
+ except pythoncom.com_error as details:
+ if details.hresult not in ERRORS_BAD_CONTEXT:
+ raise
+ return self.__repr__()
+
+ # Delegate comparison to the oleobjs, as they know how to do identity.
+ def __eq__(self, other):
+ other = getattr(other, "_oleobj_", other)
+ return self._oleobj_ == other
+
+ def __ne__(self, other):
+ other = getattr(other, "_oleobj_", other)
+ return self._oleobj_ != other
+
+ def __int__(self):
+ return int(self.__call__())
+
+ def __len__(self):
+ invkind, dispid = self._find_dispatch_type_("Count")
+ if invkind:
+ return self._oleobj_.Invoke(dispid, LCID, invkind, 1)
+ raise TypeError("This dispatch object does not define a Count method")
+
+ def _NewEnum(self):
+ try:
+ invkind = pythoncom.DISPATCH_METHOD | pythoncom.DISPATCH_PROPERTYGET
+ enum = self._oleobj_.InvokeTypes(pythoncom.DISPID_NEWENUM,LCID,invkind,(13, 10),())
+ except pythoncom.com_error:
+ return None # no enumerator for this object.
+ from . import util
+ return util.WrapEnum(enum, None)
+
+ def __getitem__(self, index): # syver modified
+ # Improved __getitem__ courtesy Syver Enstad
+ # Must check _NewEnum before Item, to ensure b/w compat.
+ if isinstance(index, int):
+ if self.__dict__['_enum_'] is None:
+ self.__dict__['_enum_'] = self._NewEnum()
+ if self.__dict__['_enum_'] is not None:
+ return self._get_good_object_(self._enum_.__getitem__(index))
+ # See if we have an "Item" method/property we can use (goes hand in hand with Count() above!)
+ invkind, dispid = self._find_dispatch_type_("Item")
+ if invkind is not None:
+ return self._get_good_object_(self._oleobj_.Invoke(dispid, LCID, invkind, 1, index))
+ raise TypeError("This object does not support enumeration")
+
+ def __setitem__(self, index, *args):
+ # XXX - todo - We should support calling Item() here too!
+# print "__setitem__ with", index, args
+ if self._olerepr_.defaultDispatchName:
+ invkind, dispid = self._find_dispatch_type_(self._olerepr_.defaultDispatchName)
+ else:
+ invkind, dispid = pythoncom.DISPATCH_PROPERTYPUT | pythoncom.DISPATCH_PROPERTYPUTREF, pythoncom.DISPID_VALUE
+ if invkind is not None:
+ allArgs = (dispid,LCID,invkind,0,index) + args
+ return self._get_good_object_(self._oleobj_.Invoke(*allArgs),self._olerepr_.defaultDispatchName,None)
+ raise TypeError("This dispatch object does not define a default method")
+
+ def _find_dispatch_type_(self, methodName):
+ if methodName in self._olerepr_.mapFuncs:
+ item = self._olerepr_.mapFuncs[methodName]
+ return item.desc[4], item.dispid
+
+ if methodName in self._olerepr_.propMapGet:
+ item = self._olerepr_.propMapGet[methodName]
+ return item.desc[4], item.dispid
+
+ try:
+ dispid = self._oleobj_.GetIDsOfNames(0,methodName)
+ except: ### what error?
+ return None, None
+ return pythoncom.DISPATCH_METHOD | pythoncom.DISPATCH_PROPERTYGET, dispid
+
+ def _ApplyTypes_(self, dispid, wFlags, retType, argTypes, user, resultCLSID, *args):
+ result = self._oleobj_.InvokeTypes(*(dispid, LCID, wFlags, retType, argTypes) + args)
+ return self._get_good_object_(result, user, resultCLSID)
+
+ def _wrap_dispatch_(self, ob, userName = None, returnCLSID = None, UnicodeToString=None):
+ # Given a dispatch object, wrap it in a class
+ assert UnicodeToString is None, "this is deprecated and will go away"
+ return Dispatch(ob, userName)
+
+ def _get_good_single_object_(self,ob,userName = None, ReturnCLSID=None):
+ if isinstance(ob, PyIDispatchType):
+ # make a new instance of (probably this) class.
+ return self._wrap_dispatch_(ob, userName, ReturnCLSID)
+ if isinstance(ob, PyIUnknownType):
+ try:
+ ob = ob.QueryInterface(pythoncom.IID_IDispatch)
+ except pythoncom.com_error:
+ # It is an IUnknown, but not an IDispatch, so just let it through.
+ return ob
+ return self._wrap_dispatch_(ob, userName, ReturnCLSID)
+ return ob
+
+ def _get_good_object_(self,ob,userName = None, ReturnCLSID=None):
+ """Given an object (usually the retval from a method), make it a good object to return.
+ Basically checks if it is a COM object, and wraps it up.
+ Also handles the fact that a retval may be a tuple of retvals"""
+ if ob is None: # Quick exit!
+ return None
+ elif isinstance(ob, tuple):
+ return tuple(map(lambda o, s=self, oun=userName, rc=ReturnCLSID: s._get_good_single_object_(o, oun, rc), ob))
+ else:
+ return self._get_good_single_object_(ob)
+
+ def _make_method_(self, name):
+ "Make a method object - Assumes in olerepr funcmap"
+ methodName = build.MakePublicAttributeName(name) # translate keywords etc.
+ methodCodeList = self._olerepr_.MakeFuncMethod(self._olerepr_.mapFuncs[name], methodName,0)
+ methodCode = "\n".join(methodCodeList)
+ try:
+# print "Method code for %s is:\n" % self._username_, methodCode
+# self._print_details_()
+ codeObject = compile(methodCode, "" % self._username_,"exec")
+ # Exec the code object
+ tempNameSpace = {}
+ # "Dispatch" in the exec'd code is win32com.client.Dispatch, not ours.
+ globNameSpace = globals().copy()
+ globNameSpace["Dispatch"] = win32com.client.Dispatch
+ exec(codeObject, globNameSpace, tempNameSpace) # self.__dict__, self.__dict__
+ name = methodName
+ # Save the function in map.
+ fn = self._builtMethods_[name] = tempNameSpace[name]
+ newMeth = MakeMethod(fn, self, self.__class__)
+ return newMeth
+ except:
+ debug_print("Error building OLE definition for code ", methodCode)
+ traceback.print_exc()
+ return None
+
+ def _Release_(self):
+ """Cleanup object - like a close - to force cleanup when you dont
+ want to rely on Python's reference counting."""
+ for childCont in self._mapCachedItems_.values():
+ childCont._Release_()
+ self._mapCachedItems_ = {}
+ if self._oleobj_:
+ self._oleobj_.Release()
+ self.__dict__['_oleobj_'] = None
+ if self._olerepr_:
+ self.__dict__['_olerepr_'] = None
+ self._enum_ = None
+
+ def _proc_(self, name, *args):
+ """Call the named method as a procedure, rather than function.
+ Mainly used by Word.Basic, which whinges about such things."""
+ try:
+ item = self._olerepr_.mapFuncs[name]
+ dispId = item.dispid
+ return self._get_good_object_(self._oleobj_.Invoke(*(dispId, LCID, item.desc[4], 0) + (args) ))
+ except KeyError:
+ raise AttributeError(name)
+
+ def _print_details_(self):
+ "Debug routine - dumps what it knows about an object."
+ print("AxDispatch container",self._username_)
+ try:
+ print("Methods:")
+ for method in self._olerepr_.mapFuncs.keys():
+ print("\t", method)
+ print("Props:")
+ for prop, entry in self._olerepr_.propMap.items():
+ print("\t%s = 0x%x - %s" % (prop, entry.dispid, repr(entry)))
+ print("Get Props:")
+ for prop, entry in self._olerepr_.propMapGet.items():
+ print("\t%s = 0x%x - %s" % (prop, entry.dispid, repr(entry)))
+ print("Put Props:")
+ for prop, entry in self._olerepr_.propMapPut.items():
+ print("\t%s = 0x%x - %s" % (prop, entry.dispid, repr(entry)))
+ except:
+ traceback.print_exc()
+
+ def __LazyMap__(self, attr):
+ try:
+ if self._LazyAddAttr_(attr):
+ debug_attr_print("%s.__LazyMap__(%s) added something" % (self._username_,attr))
+ return 1
+ except AttributeError:
+ return 0
+
+ # Using the typecomp, lazily create a new attribute definition.
+ def _LazyAddAttr_(self,attr):
+ if self._lazydata_ is None: return 0
+ res = 0
+ typeinfo, typecomp = self._lazydata_
+ olerepr = self._olerepr_
+ # We need to explicitly check each invoke type individually - simply
+ # specifying '0' will bind to "any member", which may not be the one
+ # we are actually after (ie, we may be after prop_get, but returned
+ # the info for the prop_put.)
+ for i in ALL_INVOKE_TYPES:
+ try:
+ x,t = typecomp.Bind(attr,i)
+ # Support 'Get' and 'Set' properties - see
+ # bug 1587023
+ if x==0 and attr[:3] in ('Set', 'Get'):
+ x,t = typecomp.Bind(attr[3:], i)
+ if x==1: #it's a FUNCDESC
+ r = olerepr._AddFunc_(typeinfo,t,0)
+ elif x==2: #it's a VARDESC
+ r = olerepr._AddVar_(typeinfo,t,0)
+ else: #not found or TYPEDESC/IMPLICITAPP
+ r=None
+ if not r is None:
+ key, map = r[0],r[1]
+ item = map[key]
+ if map==olerepr.propMapPut:
+ olerepr._propMapPutCheck_(key,item)
+ elif map==olerepr.propMapGet:
+ olerepr._propMapGetCheck_(key,item)
+ res = 1
+ except:
+ pass
+ return res
+
+ def _FlagAsMethod(self, *methodNames):
+ """Flag these attribute names as being methods.
+ Some objects do not correctly differentiate methods and
+ properties, leading to problems when calling these methods.
+
+ Specifically, trying to say: ob.SomeFunc()
+ may yield an exception "None object is not callable"
+ In this case, an attempt to fetch the *property*has worked
+ and returned None, rather than indicating it is really a method.
+ Calling: ob._FlagAsMethod("SomeFunc")
+ should then allow this to work.
+ """
+ for name in methodNames:
+ details = build.MapEntry(self.__AttrToID__(name), (name,))
+ self._olerepr_.mapFuncs[name] = details
+
+ def __AttrToID__(self,attr):
+ debug_attr_print("Calling GetIDsOfNames for property %s in Dispatch container %s" % (attr, self._username_))
+ return self._oleobj_.GetIDsOfNames(0,attr)
+
+ def __getattr__(self, attr):
+ if attr=='__iter__':
+ # We can't handle this as a normal method, as if the attribute
+ # exists, then it must return an iterable object.
+ try:
+ invkind = pythoncom.DISPATCH_METHOD | pythoncom.DISPATCH_PROPERTYGET
+ enum = self._oleobj_.InvokeTypes(pythoncom.DISPID_NEWENUM,LCID,invkind,(13, 10),())
+ except pythoncom.com_error:
+ raise AttributeError("This object can not function as an iterator")
+ # We must return a callable object.
+ class Factory:
+ def __init__(self, ob):
+ self.ob = ob
+ def __call__(self):
+ import win32com.client.util
+ return win32com.client.util.Iterator(self.ob)
+ return Factory(enum)
+
+ if attr.startswith('_') and attr.endswith('_'): # Fast-track.
+ raise AttributeError(attr)
+ # If a known method, create new instance and return.
+ try:
+ return MakeMethod(self._builtMethods_[attr], self, self.__class__)
+ except KeyError:
+ pass
+ # XXX - Note that we current are case sensitive in the method.
+ #debug_attr_print("GetAttr called for %s on DispatchContainer %s" % (attr,self._username_))
+ # First check if it is in the method map. Note that an actual method
+ # must not yet exist, (otherwise we would not be here). This
+ # means we create the actual method object - which also means
+ # this code will never be asked for that method name again.
+ if attr in self._olerepr_.mapFuncs:
+ return self._make_method_(attr)
+
+ # Delegate to property maps/cached items
+ retEntry = None
+ if self._olerepr_ and self._oleobj_:
+ # first check general property map, then specific "put" map.
+ retEntry = self._olerepr_.propMap.get(attr)
+ if retEntry is None:
+ retEntry = self._olerepr_.propMapGet.get(attr)
+ # Not found so far - See what COM says.
+ if retEntry is None:
+ try:
+ if self.__LazyMap__(attr):
+ if attr in self._olerepr_.mapFuncs: return self._make_method_(attr)
+ retEntry = self._olerepr_.propMap.get(attr)
+ if retEntry is None:
+ retEntry = self._olerepr_.propMapGet.get(attr)
+ if retEntry is None:
+ retEntry = build.MapEntry(self.__AttrToID__(attr), (attr,))
+ except pythoncom.ole_error:
+ pass # No prop by that name - retEntry remains None.
+
+ if not retEntry is None: # see if in my cache
+ try:
+ ret = self._mapCachedItems_[retEntry.dispid]
+ debug_attr_print ("Cached items has attribute!", ret)
+ return ret
+ except (KeyError, AttributeError):
+ debug_attr_print("Attribute %s not in cache" % attr)
+
+ # If we are still here, and have a retEntry, get the OLE item
+ if not retEntry is None:
+ invoke_type = _GetDescInvokeType(retEntry, pythoncom.INVOKE_PROPERTYGET)
+ debug_attr_print("Getting property Id 0x%x from OLE object" % retEntry.dispid)
+ try:
+ ret = self._oleobj_.Invoke(retEntry.dispid,0,invoke_type,1)
+ except pythoncom.com_error as details:
+ if details.hresult in ERRORS_BAD_CONTEXT:
+ # May be a method.
+ self._olerepr_.mapFuncs[attr] = retEntry
+ return self._make_method_(attr)
+ raise
+ debug_attr_print("OLE returned ", ret)
+ return self._get_good_object_(ret)
+
+ # no where else to look.
+ raise AttributeError("%s.%s" % (self._username_, attr))
+
+ def __setattr__(self, attr, value):
+ if attr in self.__dict__: # Fast-track - if already in our dict, just make the assignment.
+ # XXX - should maybe check method map - if someone assigns to a method,
+ # it could mean something special (not sure what, tho!)
+ self.__dict__[attr] = value
+ return
+ # Allow property assignment.
+ debug_attr_print("SetAttr called for %s.%s=%s on DispatchContainer" % (self._username_, attr, repr(value)))
+
+ if self._olerepr_:
+ # Check the "general" property map.
+ if attr in self._olerepr_.propMap:
+ entry = self._olerepr_.propMap[attr]
+ invoke_type = _GetDescInvokeType(entry, pythoncom.INVOKE_PROPERTYPUT)
+ self._oleobj_.Invoke(entry.dispid, 0, invoke_type, 0, value)
+ return
+ # Check the specific "put" map.
+ if attr in self._olerepr_.propMapPut:
+ entry = self._olerepr_.propMapPut[attr]
+ invoke_type = _GetDescInvokeType(entry, pythoncom.INVOKE_PROPERTYPUT)
+ self._oleobj_.Invoke(entry.dispid, 0, invoke_type, 0, value)
+ return
+
+ # Try the OLE Object
+ if self._oleobj_:
+ if self.__LazyMap__(attr):
+ # Check the "general" property map.
+ if attr in self._olerepr_.propMap:
+ entry = self._olerepr_.propMap[attr]
+ invoke_type = _GetDescInvokeType(entry, pythoncom.INVOKE_PROPERTYPUT)
+ self._oleobj_.Invoke(entry.dispid, 0, invoke_type, 0, value)
+ return
+ # Check the specific "put" map.
+ if attr in self._olerepr_.propMapPut:
+ entry = self._olerepr_.propMapPut[attr]
+ invoke_type = _GetDescInvokeType(entry, pythoncom.INVOKE_PROPERTYPUT)
+ self._oleobj_.Invoke(entry.dispid, 0, invoke_type, 0, value)
+ return
+ try:
+ entry = build.MapEntry(self.__AttrToID__(attr),(attr,))
+ except pythoncom.com_error:
+ # No attribute of that name
+ entry = None
+ if entry is not None:
+ try:
+ invoke_type = _GetDescInvokeType(entry, pythoncom.INVOKE_PROPERTYPUT)
+ self._oleobj_.Invoke(entry.dispid, 0, invoke_type, 0, value)
+ self._olerepr_.propMap[attr] = entry
+ debug_attr_print("__setattr__ property %s (id=0x%x) in Dispatch container %s" % (attr, entry.dispid, self._username_))
+ return
+ except pythoncom.com_error:
+ pass
+ raise AttributeError("Property '%s.%s' can not be set." % (self._username_, attr))
diff --git a/Lib/site-packages/win32com/client/gencache.py b/Lib/site-packages/win32com/client/gencache.py
new file mode 100644
index 0000000..6b61272
--- /dev/null
+++ b/Lib/site-packages/win32com/client/gencache.py
@@ -0,0 +1,698 @@
+"""Manages the cache of generated Python code.
+
+Description
+ This file manages the cache of generated Python code. When run from the
+ command line, it also provides a number of options for managing that cache.
+
+Implementation
+ Each typelib is generated into a filename of format "{guid}x{lcid}x{major}x{minor}.py"
+
+ An external persistant dictionary maps from all known IIDs in all known type libraries
+ to the type library itself.
+
+ Thus, whenever Python code knows the IID of an object, it can find the IID, LCID and version of
+ the type library which supports it. Given this information, it can find the Python module
+ with the support.
+
+ If necessary, this support can be generated on the fly.
+
+Hacks, to do, etc
+ Currently just uses a pickled dictionary, but should used some sort of indexed file.
+ Maybe an OLE2 compound file, or a bsddb file?
+"""
+import pywintypes, os, sys
+import pythoncom
+import win32com, win32com.client
+import glob
+import traceback
+from . import CLSIDToClass
+import operator
+try:
+ from imp import reload # exported by the imp module in py3k.
+except:
+ pass # a builtin on py2k.
+
+bForDemandDefault = 0 # Default value of bForDemand - toggle this to change the world - see also makepy.py
+
+# The global dictionary
+clsidToTypelib = {}
+
+# If we have a different version of the typelib generated, this
+# maps the "requested version" to the "generated version".
+versionRedirectMap = {}
+
+# There is no reason we *must* be readonly in a .zip, but we are now,
+# Rather than check for ".zip" or other tricks, PEP302 defines
+# a "__loader__" attribute, so we use that.
+# (Later, it may become necessary to check if the __loader__ can update files,
+# as a .zip loader potentially could - but punt all that until a need arises)
+is_readonly = is_zip = hasattr(win32com, "__loader__") and hasattr(win32com.__loader__, "archive")
+
+# A dictionary of ITypeLibrary objects for demand generation explicitly handed to us
+# Keyed by usual clsid, lcid, major, minor
+demandGeneratedTypeLibraries = {}
+
+import pickle as pickle
+
+def __init__():
+ # Initialize the module. Called once explicitly at module import below.
+ try:
+ _LoadDicts()
+ except IOError:
+ Rebuild()
+
+pickleVersion = 1
+def _SaveDicts():
+ if is_readonly:
+ raise RuntimeError("Trying to write to a readonly gencache ('%s')!" \
+ % win32com.__gen_path__)
+ f = open(os.path.join(GetGeneratePath(), "dicts.dat"), "wb")
+ try:
+ p = pickle.Pickler(f)
+ p.dump(pickleVersion)
+ p.dump(clsidToTypelib)
+ finally:
+ f.close()
+
+def _LoadDicts():
+ # Load the dictionary from a .zip file if that is where we live.
+ if is_zip:
+ import io as io
+ loader = win32com.__loader__
+ arc_path = loader.archive
+ dicts_path = os.path.join(win32com.__gen_path__, "dicts.dat")
+ if dicts_path.startswith(arc_path):
+ dicts_path = dicts_path[len(arc_path)+1:]
+ else:
+ # Hm. See below.
+ return
+ try:
+ data = loader.get_data(dicts_path)
+ except AttributeError:
+ # The __loader__ has no get_data method. See below.
+ return
+ except IOError:
+ # Our gencache is in a .zip file (and almost certainly readonly)
+ # but no dicts file. That actually needn't be fatal for a frozen
+ # application. Assuming they call "EnsureModule" with the same
+ # typelib IDs they have been frozen with, that EnsureModule will
+ # correctly re-build the dicts on the fly. However, objects that
+ # rely on the gencache but have not done an EnsureModule will
+ # fail (but their apps are likely to fail running from source
+ # with a clean gencache anyway, as then they would be getting
+ # Dynamic objects until the cache is built - so the best answer
+ # for these apps is to call EnsureModule, rather than freezing
+ # the dict)
+ return
+ f = io.StringIO(data)
+ else:
+ # NOTE: IOError on file open must be caught by caller.
+ f = open(os.path.join(win32com.__gen_path__, "dicts.dat"), "rb")
+ try:
+ p = pickle.Unpickler(f)
+ version = p.load()
+ global clsidToTypelib
+ clsidToTypelib = p.load()
+ versionRedirectMap.clear()
+ finally:
+ f.close()
+
+def GetGeneratedFileName(clsid, lcid, major, minor):
+ """Given the clsid, lcid, major and minor for a type lib, return
+ the file name (no extension) providing this support.
+ """
+ return str(clsid).upper()[1:-1] + "x%sx%sx%s" % (lcid, major, minor)
+
+def SplitGeneratedFileName(fname):
+ """Reverse of GetGeneratedFileName()
+ """
+ return tuple(fname.split('x',4))
+
+def GetGeneratePath():
+ """Returns the name of the path to generate to.
+ Checks the directory is OK.
+ """
+ assert not is_readonly, "Why do you want the genpath for a readonly store?"
+ try:
+ os.makedirs(win32com.__gen_path__)
+ #os.mkdir(win32com.__gen_path__)
+ except os.error:
+ pass
+ try:
+ fname = os.path.join(win32com.__gen_path__, "__init__.py")
+ os.stat(fname)
+ except os.error:
+ f = open(fname,"w")
+ f.write('# Generated file - this directory may be deleted to reset the COM cache...\n')
+ f.write('import win32com\n')
+ f.write('if __path__[:-1] != win32com.__gen_path__: __path__.append(win32com.__gen_path__)\n')
+ f.close()
+
+ return win32com.__gen_path__
+
+#
+# The helpers for win32com.client.Dispatch and OCX clients.
+#
+def GetClassForProgID(progid):
+ """Get a Python class for a Program ID
+
+ Given a Program ID, return a Python class which wraps the COM object
+
+ Returns the Python class, or None if no module is available.
+
+ Params
+ progid -- A COM ProgramID or IID (eg, "Word.Application")
+ """
+ clsid = pywintypes.IID(progid) # This auto-converts named to IDs.
+ return GetClassForCLSID(clsid)
+
+def GetClassForCLSID(clsid):
+ """Get a Python class for a CLSID
+
+ Given a CLSID, return a Python class which wraps the COM object
+
+ Returns the Python class, or None if no module is available.
+
+ Params
+ clsid -- A COM CLSID (or string repr of one)
+ """
+ # first, take a short-cut - we may already have generated support ready-to-roll.
+ clsid = str(clsid)
+ if CLSIDToClass.HasClass(clsid):
+ return CLSIDToClass.GetClass(clsid)
+ mod = GetModuleForCLSID(clsid)
+ if mod is None:
+ return None
+ try:
+ return CLSIDToClass.GetClass(clsid)
+ except KeyError:
+ return None
+
+def GetModuleForProgID(progid):
+ """Get a Python module for a Program ID
+
+ Given a Program ID, return a Python module which contains the
+ class which wraps the COM object.
+
+ Returns the Python module, or None if no module is available.
+
+ Params
+ progid -- A COM ProgramID or IID (eg, "Word.Application")
+ """
+ try:
+ iid = pywintypes.IID(progid)
+ except pywintypes.com_error:
+ return None
+ return GetModuleForCLSID(iid)
+
+def GetModuleForCLSID(clsid):
+ """Get a Python module for a CLSID
+
+ Given a CLSID, return a Python module which contains the
+ class which wraps the COM object.
+
+ Returns the Python module, or None if no module is available.
+
+ Params
+ progid -- A COM CLSID (ie, not the description)
+ """
+ clsid_str = str(clsid)
+ try:
+ typelibCLSID, lcid, major, minor = clsidToTypelib[clsid_str]
+ except KeyError:
+ return None
+
+ try:
+ mod = GetModuleForTypelib(typelibCLSID, lcid, major, minor)
+ except ImportError:
+ mod = None
+ if mod is not None:
+ sub_mod = mod.CLSIDToPackageMap.get(clsid_str)
+ if sub_mod is None:
+ sub_mod = mod.VTablesToPackageMap.get(clsid_str)
+ if sub_mod is not None:
+ sub_mod_name = mod.__name__ + "." + sub_mod
+ try:
+ __import__(sub_mod_name)
+ except ImportError:
+ info = typelibCLSID, lcid, major, minor
+ # Force the generation. If this typelibrary has explicitly been added,
+ # use it (it may not be registered, causing a lookup by clsid to fail)
+ if info in demandGeneratedTypeLibraries:
+ info = demandGeneratedTypeLibraries[info]
+ from . import makepy
+ makepy.GenerateChildFromTypeLibSpec(sub_mod, info)
+ # Generate does an import...
+ mod = sys.modules[sub_mod_name]
+ return mod
+
+def GetModuleForTypelib(typelibCLSID, lcid, major, minor):
+ """Get a Python module for a type library ID
+
+ Given the CLSID of a typelibrary, return an imported Python module,
+ else None
+
+ Params
+ typelibCLSID -- IID of the type library.
+ major -- Integer major version.
+ minor -- Integer minor version
+ lcid -- Integer LCID for the library.
+ """
+ modName = GetGeneratedFileName(typelibCLSID, lcid, major, minor)
+ mod = _GetModule(modName)
+ # If the import worked, it doesn't mean we have actually added this
+ # module to our cache though - check that here.
+ if "_in_gencache_" not in mod.__dict__:
+ AddModuleToCache(typelibCLSID, lcid, major, minor)
+ assert "_in_gencache_" in mod.__dict__
+ return mod
+
+def MakeModuleForTypelib(typelibCLSID, lcid, major, minor, progressInstance = None, bGUIProgress = None, bForDemand = bForDemandDefault, bBuildHidden = 1):
+ """Generate support for a type library.
+
+ Given the IID, LCID and version information for a type library, generate
+ and import the necessary support files.
+
+ Returns the Python module. No exceptions are caught.
+
+ Params
+ typelibCLSID -- IID of the type library.
+ major -- Integer major version.
+ minor -- Integer minor version.
+ lcid -- Integer LCID for the library.
+ progressInstance -- Instance to use as progress indicator, or None to
+ use the GUI progress bar.
+ """
+ if bGUIProgress is not None:
+ print("The 'bGuiProgress' param to 'MakeModuleForTypelib' is obsolete.")
+
+ from . import makepy
+ try:
+ makepy.GenerateFromTypeLibSpec( (typelibCLSID, lcid, major, minor), progressInstance=progressInstance, bForDemand = bForDemand, bBuildHidden = bBuildHidden)
+ except pywintypes.com_error:
+ return None
+ return GetModuleForTypelib(typelibCLSID, lcid, major, minor)
+
+def MakeModuleForTypelibInterface(typelib_ob, progressInstance = None, bForDemand = bForDemandDefault, bBuildHidden = 1):
+ """Generate support for a type library.
+
+ Given a PyITypeLib interface generate and import the necessary support files. This is useful
+ for getting makepy support for a typelibrary that is not registered - the caller can locate
+ and load the type library itself, rather than relying on COM to find it.
+
+ Returns the Python module.
+
+ Params
+ typelib_ob -- The type library itself
+ progressInstance -- Instance to use as progress indicator, or None to
+ use the GUI progress bar.
+ """
+ from . import makepy
+ try:
+ makepy.GenerateFromTypeLibSpec( typelib_ob, progressInstance=progressInstance, bForDemand = bForDemandDefault, bBuildHidden = bBuildHidden)
+ except pywintypes.com_error:
+ return None
+ tla = typelib_ob.GetLibAttr()
+ guid = tla[0]
+ lcid = tla[1]
+ major = tla[3]
+ minor = tla[4]
+ return GetModuleForTypelib(guid, lcid, major, minor)
+
+def EnsureModuleForTypelibInterface(typelib_ob, progressInstance = None, bForDemand = bForDemandDefault, bBuildHidden = 1):
+ """Check we have support for a type library, generating if not.
+
+ Given a PyITypeLib interface generate and import the necessary
+ support files if necessary. This is useful for getting makepy support
+ for a typelibrary that is not registered - the caller can locate and
+ load the type library itself, rather than relying on COM to find it.
+
+ Returns the Python module.
+
+ Params
+ typelib_ob -- The type library itself
+ progressInstance -- Instance to use as progress indicator, or None to
+ use the GUI progress bar.
+ """
+ tla = typelib_ob.GetLibAttr()
+ guid = tla[0]
+ lcid = tla[1]
+ major = tla[3]
+ minor = tla[4]
+
+ #If demand generated, save the typelib interface away for later use
+ if bForDemand:
+ demandGeneratedTypeLibraries[(str(guid), lcid, major, minor)] = typelib_ob
+
+ try:
+ return GetModuleForTypelib(guid, lcid, major, minor)
+ except ImportError:
+ pass
+ # Generate it.
+ return MakeModuleForTypelibInterface(typelib_ob, progressInstance, bForDemand, bBuildHidden)
+
+def ForgetAboutTypelibInterface(typelib_ob):
+ """Drop any references to a typelib previously added with EnsureModuleForTypelibInterface and forDemand"""
+ tla = typelib_ob.GetLibAttr()
+ guid = tla[0]
+ lcid = tla[1]
+ major = tla[3]
+ minor = tla[4]
+ info = str(guid), lcid, major, minor
+ try:
+ del demandGeneratedTypeLibraries[info]
+ except KeyError:
+ # Not worth raising an exception - maybe they dont know we only remember for demand generated, etc.
+ print("ForgetAboutTypelibInterface:: Warning - type library with info %s is not being remembered!" % (info,))
+ # and drop any version redirects to it
+ for key, val in list(versionRedirectMap.items()):
+ if val==info:
+ del versionRedirectMap[key]
+
+def EnsureModule(typelibCLSID, lcid, major, minor, progressInstance = None, bValidateFile=not is_readonly, bForDemand = bForDemandDefault, bBuildHidden = 1):
+ """Ensure Python support is loaded for a type library, generating if necessary.
+
+ Given the IID, LCID and version information for a type library, check and if
+ necessary (re)generate, then import the necessary support files. If we regenerate the file, there
+ is no way to totally snuff out all instances of the old module in Python, and thus we will regenerate the file more than necessary,
+ unless makepy/genpy is modified accordingly.
+
+
+ Returns the Python module. No exceptions are caught during the generate process.
+
+ Params
+ typelibCLSID -- IID of the type library.
+ major -- Integer major version.
+ minor -- Integer minor version
+ lcid -- Integer LCID for the library.
+ progressInstance -- Instance to use as progress indicator, or None to
+ use the GUI progress bar.
+ bValidateFile -- Whether or not to perform cache validation or not
+ bForDemand -- Should a complete generation happen now, or on demand?
+ bBuildHidden -- Should hidden members/attributes etc be generated?
+ """
+ bReloadNeeded = 0
+ try:
+ try:
+ module = GetModuleForTypelib(typelibCLSID, lcid, major, minor)
+ except ImportError:
+ # If we get an ImportError
+ # We may still find a valid cache file under a different MinorVersion #
+ # (which windows will search out for us)
+ #print "Loading reg typelib", typelibCLSID, major, minor, lcid
+ module = None
+ try:
+ tlbAttr = pythoncom.LoadRegTypeLib(typelibCLSID, major, minor, lcid).GetLibAttr()
+ # if the above line doesn't throw a pythoncom.com_error, check if
+ # it is actually a different lib than we requested, and if so, suck it in
+ if tlbAttr[1] != lcid or tlbAttr[4]!=minor:
+ #print "Trying 2nd minor #", tlbAttr[1], tlbAttr[3], tlbAttr[4]
+ try:
+ module = GetModuleForTypelib(typelibCLSID, tlbAttr[1], tlbAttr[3], tlbAttr[4])
+ except ImportError:
+ # We don't have a module, but we do have a better minor
+ # version - remember that.
+ minor = tlbAttr[4]
+ # else module remains None
+ except pythoncom.com_error:
+ # couldn't load any typelib - mod remains None
+ pass
+ if module is not None and bValidateFile:
+ assert not is_readonly, "Can't validate in a read-only gencache"
+ try:
+ typLibPath = pythoncom.QueryPathOfRegTypeLib(typelibCLSID, major, minor, lcid)
+ # windows seems to add an extra \0 (via the underlying BSTR)
+ # The mainwin toolkit does not add this erroneous \0
+ if typLibPath[-1]=='\0':
+ typLibPath=typLibPath[:-1]
+ suf = getattr(os.path, "supports_unicode_filenames", 0)
+ if not suf:
+ # can't pass unicode filenames directly - convert
+ try:
+ typLibPath=typLibPath.encode(sys.getfilesystemencoding())
+ except AttributeError: # no sys.getfilesystemencoding
+ typLibPath=str(typLibPath)
+ tlbAttributes = pythoncom.LoadRegTypeLib(typelibCLSID, major, minor, lcid).GetLibAttr()
+ except pythoncom.com_error:
+ # We have a module, but no type lib - we should still
+ # run with what we have though - the typelib may not be
+ # deployed here.
+ bValidateFile = 0
+ if module is not None and bValidateFile:
+ assert not is_readonly, "Can't validate in a read-only gencache"
+ filePathPrefix = "%s\\%s" % (GetGeneratePath(), GetGeneratedFileName(typelibCLSID, lcid, major, minor))
+ filePath = filePathPrefix + ".py"
+ filePathPyc = filePathPrefix + ".py"
+ if __debug__:
+ filePathPyc = filePathPyc + "c"
+ else:
+ filePathPyc = filePathPyc + "o"
+ # Verify that type library is up to date.
+ # If we have a differing MinorVersion or genpy has bumped versions, update the file
+ from . import genpy
+ if module.MinorVersion != tlbAttributes[4] or genpy.makepy_version != module.makepy_version:
+ #print "Version skew: %d, %d" % (module.MinorVersion, tlbAttributes[4])
+ # try to erase the bad file from the cache
+ try:
+ os.unlink(filePath)
+ except os.error:
+ pass
+ try:
+ os.unlink(filePathPyc)
+ except os.error:
+ pass
+ if os.path.isdir(filePathPrefix):
+ import shutil
+ shutil.rmtree(filePathPrefix)
+ minor = tlbAttributes[4]
+ module = None
+ bReloadNeeded = 1
+ else:
+ minor = module.MinorVersion
+ filePathPrefix = "%s\\%s" % (GetGeneratePath(), GetGeneratedFileName(typelibCLSID, lcid, major, minor))
+ filePath = filePathPrefix + ".py"
+ filePathPyc = filePathPrefix + ".pyc"
+ #print "Trying py stat: ", filePath
+ fModTimeSet = 0
+ try:
+ pyModTime = os.stat(filePath)[8]
+ fModTimeSet = 1
+ except os.error as e:
+ # If .py file fails, try .pyc file
+ #print "Trying pyc stat", filePathPyc
+ try:
+ pyModTime = os.stat(filePathPyc)[8]
+ fModTimeSet = 1
+ except os.error as e:
+ pass
+ #print "Trying stat typelib", pyModTime
+ #print str(typLibPath)
+ typLibModTime = os.stat(typLibPath)[8]
+ if fModTimeSet and (typLibModTime > pyModTime):
+ bReloadNeeded = 1
+ module = None
+ except (ImportError, os.error):
+ module = None
+ if module is None:
+ # We need to build an item. If we are in a read-only cache, we
+ # can't/don't want to do this - so before giving up, check for
+ # a different minor version in our cache - according to COM, this is OK
+ if is_readonly:
+ key = str(typelibCLSID), lcid, major, minor
+ # If we have been asked before, get last result.
+ try:
+ return versionRedirectMap[key]
+ except KeyError:
+ pass
+ # Find other candidates.
+ items = []
+ for desc in GetGeneratedInfos():
+ if key[0]==desc[0] and key[1]==desc[1] and key[2]==desc[2]:
+ items.append(desc)
+ if items:
+ # Items are all identical, except for last tuple element
+ # We want the latest minor version we have - so just sort and grab last
+ items.sort()
+ new_minor = items[-1][3]
+ ret = GetModuleForTypelib(typelibCLSID, lcid, major, new_minor)
+ else:
+ ret = None
+ # remember and return
+ versionRedirectMap[key] = ret
+ return ret
+ #print "Rebuilding: ", major, minor
+ module = MakeModuleForTypelib(typelibCLSID, lcid, major, minor, progressInstance, bForDemand = bForDemand, bBuildHidden = bBuildHidden)
+ # If we replaced something, reload it
+ if bReloadNeeded:
+ module = reload(module)
+ AddModuleToCache(typelibCLSID, lcid, major, minor)
+ return module
+
+def EnsureDispatch(prog_id, bForDemand = 1): # New fn, so we default the new demand feature to on!
+ """Given a COM prog_id, return an object that is using makepy support, building if necessary"""
+ disp = win32com.client.Dispatch(prog_id)
+ if not disp.__dict__.get("CLSID"): # Eeek - no makepy support - try and build it.
+ try:
+ ti = disp._oleobj_.GetTypeInfo()
+ disp_clsid = ti.GetTypeAttr()[0]
+ tlb, index = ti.GetContainingTypeLib()
+ tla = tlb.GetLibAttr()
+ mod = EnsureModule(tla[0], tla[1], tla[3], tla[4], bForDemand=bForDemand)
+ GetModuleForCLSID(disp_clsid)
+ # Get the class from the module.
+ from . import CLSIDToClass
+ disp_class = CLSIDToClass.GetClass(str(disp_clsid))
+ disp = disp_class(disp._oleobj_)
+ except pythoncom.com_error:
+ raise TypeError("This COM object can not automate the makepy process - please run makepy manually for this object")
+ return disp
+
+def AddModuleToCache(typelibclsid, lcid, major, minor, verbose = 1, bFlushNow = not is_readonly):
+ """Add a newly generated file to the cache dictionary.
+ """
+ fname = GetGeneratedFileName(typelibclsid, lcid, major, minor)
+ mod = _GetModule(fname)
+ # if mod._in_gencache_ is already true, then we are reloading this
+ # module - this doesn't mean anything special though!
+ mod._in_gencache_ = 1
+ dict = mod.CLSIDToClassMap
+ info = str(typelibclsid), lcid, major, minor
+ for clsid, cls in dict.items():
+ clsidToTypelib[clsid] = info
+
+ dict = mod.CLSIDToPackageMap
+ for clsid, name in dict.items():
+ clsidToTypelib[clsid] = info
+
+ dict = mod.VTablesToClassMap
+ for clsid, cls in dict.items():
+ clsidToTypelib[clsid] = info
+
+ dict = mod.VTablesToPackageMap
+ for clsid, cls in dict.items():
+ clsidToTypelib[clsid] = info
+
+ # If this lib was previously redirected, drop it
+ if info in versionRedirectMap:
+ del versionRedirectMap[info]
+ if bFlushNow:
+ _SaveDicts()
+
+def GetGeneratedInfos():
+ zip_pos = win32com.__gen_path__.find(".zip\\")
+ if zip_pos >= 0:
+ import zipfile
+ zip_file = win32com.__gen_path__[:zip_pos+4]
+ zip_path = win32com.__gen_path__[zip_pos+5:].replace("\\", "/")
+ zf = zipfile.ZipFile(zip_file)
+ infos = {}
+ for n in zf.namelist():
+ if not n.startswith(zip_path):
+ continue
+ base = n[len(zip_path)+1:].split("/")[0]
+ try:
+ iid, lcid, major, minor = base.split("x")
+ lcid = int(lcid)
+ major = int(major)
+ minor = int(minor)
+ iid = pywintypes.IID("{" + iid + "}")
+ except ValueError:
+ continue
+ except pywintypes.com_error:
+ # invalid IID
+ continue
+ infos[(iid, lcid, major, minor)] = 1
+ zf.close()
+ return list(infos.keys())
+ else:
+ # on the file system
+ files = glob.glob(win32com.__gen_path__+ "\\*")
+ ret = []
+ for file in files:
+ if not os.path.isdir(file) and not os.path.splitext(file)[1]==".py":
+ continue
+ name = os.path.splitext(os.path.split(file)[1])[0]
+ try:
+ iid, lcid, major, minor = name.split("x")
+ iid = pywintypes.IID("{" + iid + "}")
+ lcid = int(lcid)
+ major = int(major)
+ minor = int(minor)
+ except ValueError:
+ continue
+ except pywintypes.com_error:
+ # invalid IID
+ continue
+ ret.append((iid, lcid, major, minor))
+ return ret
+
+def _GetModule(fname):
+ """Given the name of a module in the gen_py directory, import and return it.
+ """
+ mod_name = "win32com.gen_py.%s" % fname
+ mod = __import__(mod_name)
+ return sys.modules[mod_name]
+
+def Rebuild(verbose = 1):
+ """Rebuild the cache indexes from the file system.
+ """
+ clsidToTypelib.clear()
+ infos = GetGeneratedInfos()
+ if verbose and len(infos): # Dont bother reporting this when directory is empty!
+ print("Rebuilding cache of generated files for COM support...")
+ for info in infos:
+ iid, lcid, major, minor = info
+ if verbose:
+ print("Checking", GetGeneratedFileName(*info))
+ try:
+ AddModuleToCache(iid, lcid, major, minor, verbose, 0)
+ except:
+ print("Could not add module %s - %s: %s" % (info, sys.exc_info()[0],sys.exc_info()[1]))
+ if verbose and len(infos): # Dont bother reporting this when directory is empty!
+ print("Done.")
+ _SaveDicts()
+
+def _Dump():
+ print("Cache is in directory", win32com.__gen_path__)
+ # Build a unique dir
+ d = {}
+ for clsid, (typelibCLSID, lcid, major, minor) in clsidToTypelib.items():
+ d[typelibCLSID, lcid, major, minor] = None
+ for typelibCLSID, lcid, major, minor in d.keys():
+ mod = GetModuleForTypelib(typelibCLSID, lcid, major, minor)
+ print("%s - %s" % (mod.__doc__, typelibCLSID))
+
+# Boot up
+__init__()
+
+def usage():
+ usageString = """\
+ Usage: gencache [-q] [-d] [-r]
+
+ -q - Quiet
+ -d - Dump the cache (typelibrary description and filename).
+ -r - Rebuild the cache dictionary from the existing .py files
+ """
+ print(usageString)
+ sys.exit(1)
+
+if __name__=='__main__':
+ import getopt
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "qrd")
+ except getopt.error as message:
+ print(message)
+ usage()
+
+ # we only have options - complain about real args, or none at all!
+ if len(sys.argv)==1 or args:
+ print(usage())
+
+ verbose = 1
+ for opt, val in opts:
+ if opt=='-d': # Dump
+ _Dump()
+ if opt=='-r':
+ Rebuild(verbose)
+ if opt=='-q':
+ verbose = 0
diff --git a/Lib/site-packages/win32com/client/genpy.py b/Lib/site-packages/win32com/client/genpy.py
new file mode 100644
index 0000000..451461d
--- /dev/null
+++ b/Lib/site-packages/win32com/client/genpy.py
@@ -0,0 +1,1074 @@
+"""genpy.py - The worker for makepy. See makepy.py for more details
+
+This code was moved simply to speed Python in normal circumstances. As the makepy.py
+is normally run from the command line, it reparses the code each time. Now makepy
+is nothing more than the command line handler and public interface.
+
+The makepy command line etc handling is also getting large enough in its own right!
+"""
+
+# NOTE - now supports a "demand" mechanism - the top-level is a package, and
+# each class etc can be made individually.
+# This should eventually become the default.
+# Then the old non-package technique should be removed.
+# There should be no b/w compat issues, and will just help clean the code.
+# This will be done once the new "demand" mechanism gets a good workout.
+import os
+import sys
+import time
+import win32com
+
+import pythoncom
+from . import build
+
+error = "makepy.error"
+makepy_version = "0.5.01" # Written to generated file.
+
+GEN_FULL="full"
+GEN_DEMAND_BASE = "demand(base)"
+GEN_DEMAND_CHILD = "demand(child)"
+
+# This map is used purely for the users benefit -it shows the
+# raw, underlying type of Alias/Enums, etc. The COM implementation
+# does not use this map at runtime - all Alias/Enum have already
+# been translated.
+mapVTToTypeString = {
+ pythoncom.VT_I2: 'types.IntType',
+ pythoncom.VT_I4: 'types.IntType',
+ pythoncom.VT_R4: 'types.FloatType',
+ pythoncom.VT_R8: 'types.FloatType',
+ pythoncom.VT_BSTR: 'types.StringType',
+ pythoncom.VT_BOOL: 'types.IntType',
+ pythoncom.VT_VARIANT: 'types.TypeType',
+ pythoncom.VT_I1: 'types.IntType',
+ pythoncom.VT_UI1: 'types.IntType',
+ pythoncom.VT_UI2: 'types.IntType',
+ pythoncom.VT_UI4: 'types.IntType',
+ pythoncom.VT_I8: 'types.LongType',
+ pythoncom.VT_UI8: 'types.LongType',
+ pythoncom.VT_INT: 'types.IntType',
+ pythoncom.VT_DATE: 'pythoncom.PyTimeType',
+ pythoncom.VT_UINT: 'types.IntType',
+}
+
+# Given a propget function's arg desc, return the default parameters for all
+# params bar the first. Eg, then Python does a:
+# object.Property = "foo"
+# Python can only pass the "foo" value. If the property has
+# multiple args, and the rest have default values, this allows
+# Python to correctly pass those defaults.
+def MakeDefaultArgsForPropertyPut(argsDesc):
+ ret = []
+ for desc in argsDesc[1:]:
+ default = build.MakeDefaultArgRepr(desc)
+ if default is None:
+ break
+ ret.append(default)
+ return tuple(ret)
+
+
+def MakeMapLineEntry(dispid, wFlags, retType, argTypes, user, resultCLSID):
+ # Strip the default value
+ argTypes = tuple([what[:2] for what in argTypes])
+ return '(%s, %d, %s, %s, "%s", %s)' % \
+ (dispid, wFlags, retType[:2], argTypes, user, resultCLSID)
+
+def MakeEventMethodName(eventName):
+ if eventName[:2]=="On":
+ return eventName
+ else:
+ return "On"+eventName
+
+def WriteSinkEventMap(obj, stream):
+ print('\t_dispid_to_func_ = {', file=stream)
+ for name, entry in list(obj.propMapGet.items()) + list(obj.propMapPut.items()) + list(obj.mapFuncs.items()):
+ fdesc = entry.desc
+ print('\t\t%9d : "%s",' % (entry.desc[0], MakeEventMethodName(entry.names[0])), file=stream)
+ print('\t\t}', file=stream)
+
+
+# MI is used to join my writable helpers, and the OLE
+# classes.
+class WritableItem:
+ # __cmp__ used for sorting in py2x...
+ def __cmp__(self, other):
+ "Compare for sorting"
+ ret = cmp(self.order, other.order)
+ if ret==0 and self.doc: ret = cmp(self.doc[0], other.doc[0])
+ return ret
+ # ... but not used in py3k - __lt__ minimum needed there
+ def __lt__(self, other): # py3k variant
+ if self.order == other.order:
+ return self.doc < other.doc
+ return self.order < other.order
+ def __repr__(self):
+ return "OleItem: doc=%s, order=%d" % (repr(self.doc), self.order)
+
+
+class RecordItem(build.OleItem, WritableItem):
+ order = 9
+ typename = "RECORD"
+
+ def __init__(self, typeInfo, typeAttr, doc=None, bForUser=1):
+## sys.stderr.write("Record %s: size %s\n" % (doc,typeAttr.cbSizeInstance))
+## sys.stderr.write(" cVars = %s\n" % (typeAttr.cVars,))
+## for i in range(typeAttr.cVars):
+## vdesc = typeInfo.GetVarDesc(i)
+## sys.stderr.write(" Var %d has value %s, type %d, desc=%s\n" % (i, vdesc.value, vdesc.varkind, vdesc.elemdescVar))
+## sys.stderr.write(" Doc is %s\n" % (typeInfo.GetDocumentation(vdesc.memid),))
+
+ build.OleItem.__init__(self, doc)
+ self.clsid = typeAttr[0]
+
+ def WriteClass(self, generator):
+ pass
+
+# Given an enum, write all aliases for it.
+# (no longer necessary for new style code, but still used for old code.
+def WriteAliasesForItem(item, aliasItems, stream):
+ for alias in aliasItems.values():
+ if item.doc and alias.aliasDoc and (alias.aliasDoc[0]==item.doc[0]):
+ alias.WriteAliasItem(aliasItems, stream)
+
+class AliasItem(build.OleItem, WritableItem):
+ order = 2
+ typename = "ALIAS"
+
+ def __init__(self, typeinfo, attr, doc=None, bForUser = 1):
+ build.OleItem.__init__(self, doc)
+
+ ai = attr[14]
+ self.attr = attr
+ if type(ai) == type(()) and \
+ type(ai[1])==type(0): # XXX - This is a hack - why tuples? Need to resolve?
+ href = ai[1]
+ alinfo = typeinfo.GetRefTypeInfo(href)
+ self.aliasDoc = alinfo.GetDocumentation(-1)
+ self.aliasAttr = alinfo.GetTypeAttr()
+ else:
+ self.aliasDoc = None
+ self.aliasAttr = None
+
+ def WriteAliasItem(self, aliasDict, stream):
+ # we could have been written as part of an alias dependency
+ if self.bWritten:
+ return
+
+ if self.aliasDoc:
+ depName = self.aliasDoc[0]
+ if depName in aliasDict:
+ aliasDict[depName].WriteAliasItem(aliasDict, stream)
+ print(self.doc[0] + " = " + depName, file=stream)
+ else:
+ ai = self.attr[14]
+ if type(ai) == type(0):
+ try:
+ typeStr = mapVTToTypeString[ai]
+ print("# %s=%s" % (self.doc[0], typeStr), file=stream)
+ except KeyError:
+ print(self.doc[0] + " = None # Can't convert alias info " + str(ai), file=stream)
+ print(file=stream)
+ self.bWritten = 1
+
+class EnumerationItem(build.OleItem, WritableItem):
+ order = 1
+ typename = "ENUMERATION"
+
+ def __init__(self, typeinfo, attr, doc=None, bForUser=1):
+ build.OleItem.__init__(self, doc)
+
+ self.clsid = attr[0]
+ self.mapVars = {}
+ typeFlags = attr[11]
+ self.hidden = typeFlags & pythoncom.TYPEFLAG_FHIDDEN or \
+ typeFlags & pythoncom.TYPEFLAG_FRESTRICTED
+
+ for j in range(attr[7]):
+ vdesc = typeinfo.GetVarDesc(j)
+ name = typeinfo.GetNames(vdesc[0])[0]
+ self.mapVars[name] = build.MapEntry(vdesc)
+
+## def WriteEnumerationHeaders(self, aliasItems, stream):
+## enumName = self.doc[0]
+## print >> stream "%s=constants # Compatibility with previous versions." % (enumName)
+## WriteAliasesForItem(self, aliasItems)
+
+ def WriteEnumerationItems(self, stream):
+ num = 0
+ enumName = self.doc[0]
+ # Write in name alpha order
+ names = list(self.mapVars.keys())
+ names.sort()
+ for name in names:
+ entry = self.mapVars[name]
+ vdesc = entry.desc
+ if vdesc[4] == pythoncom.VAR_CONST:
+ val = vdesc[1]
+
+ use = repr(val)
+ # Make sure the repr of the value is valid python syntax
+ # still could cause an error on import if it contains a module or type name
+ # not available in the global namespace
+ try:
+ compile(use, '', 'eval')
+ except SyntaxError:
+ # At least add the repr as a string, so it can be investigated further
+ # Sanitize it, in case the repr contains its own quotes. (??? line breaks too ???)
+ use = use.replace('"',"'")
+ use = '"' + use + '"' + ' # This VARIANT type cannot be converted automatically'
+ print("\t%-30s=%-10s # from enum %s" % \
+ (build.MakePublicAttributeName(name, True), use, enumName), file=stream)
+ num += 1
+ return num
+
+class VTableItem(build.VTableItem, WritableItem):
+ order = 4
+
+ def WriteClass(self, generator):
+ self.WriteVTableMap(generator)
+ self.bWritten = 1
+
+ def WriteVTableMap(self, generator):
+ stream = generator.file
+ print("%s_vtables_dispatch_ = %d" % (self.python_name, self.bIsDispatch), file=stream)
+ print("%s_vtables_ = [" % (self.python_name, ), file=stream)
+ for v in self.vtableFuncs:
+ names, dispid, desc = v
+ arg_desc = desc[2]
+
+ arg_reprs = []
+ # more hoops so we don't generate huge lines.
+ item_num = 0
+ print("\t((", end=' ', file=stream)
+ for name in names:
+ print(repr(name), ",", end=' ', file=stream)
+ item_num = item_num + 1
+ if item_num % 5 == 0:
+ print("\n\t\t\t", end=' ', file=stream)
+ print("), %d, (%r, %r, [" % (dispid, desc[0], desc[1]), end=' ', file=stream)
+ for arg in arg_desc:
+ item_num = item_num + 1
+ if item_num % 5 == 0:
+ print("\n\t\t\t", end=' ', file=stream)
+ defval = build.MakeDefaultArgRepr(arg)
+ if arg[3] is None:
+ arg3_repr = None
+ else:
+ arg3_repr = repr(arg[3])
+ print(repr((arg[0], arg[1], defval, arg3_repr)), ",", end=' ', file=stream)
+ print("],", end=' ', file=stream)
+ for d in desc[3:]:
+ print(repr(d), ",", end=' ', file=stream)
+ print(")),", file=stream)
+ print("]", file=stream)
+ print(file=stream)
+
+class DispatchItem(build.DispatchItem, WritableItem):
+ order = 3
+
+ def __init__(self, typeinfo, attr, doc=None):
+ build.DispatchItem.__init__(self, typeinfo, attr, doc)
+ self.type_attr = attr
+ self.coclass_clsid = None
+
+ def WriteClass(self, generator):
+ if not self.bIsDispatch and not self.type_attr.typekind == pythoncom.TKIND_DISPATCH:
+ return
+ # This is pretty screwey - now we have vtable support we
+ # should probably rethink this (ie, maybe write both sides for sinks, etc)
+ if self.bIsSink:
+ self.WriteEventSinkClassHeader(generator)
+ self.WriteCallbackClassBody(generator)
+ else:
+ self.WriteClassHeader(generator)
+ self.WriteClassBody(generator)
+ print(file=generator.file)
+ self.bWritten = 1
+
+ def WriteClassHeader(self, generator):
+ generator.checkWriteDispatchBaseClass()
+ doc = self.doc
+ stream = generator.file
+ print('class ' + self.python_name + '(DispatchBaseClass):', file=stream)
+ if doc[1]: print('\t' + build._makeDocString(doc[1]), file=stream)
+ try:
+ progId = pythoncom.ProgIDFromCLSID(self.clsid)
+ print("\t# This class is creatable by the name '%s'" % (progId), file=stream)
+ except pythoncom.com_error:
+ pass
+ print("\tCLSID = " + repr(self.clsid), file=stream)
+ if self.coclass_clsid is None:
+ print("\tcoclass_clsid = None", file=stream)
+ else:
+ print("\tcoclass_clsid = " + repr(self.coclass_clsid), file=stream)
+ print(file=stream)
+ self.bWritten = 1
+
+ def WriteEventSinkClassHeader(self, generator):
+ generator.checkWriteEventBaseClass()
+ doc = self.doc
+ stream = generator.file
+ print('class ' + self.python_name + ':', file=stream)
+ if doc[1]: print('\t' + build._makeDocString(doc[1]), file=stream)
+ try:
+ progId = pythoncom.ProgIDFromCLSID(self.clsid)
+ print("\t# This class is creatable by the name '%s'" % (progId), file=stream)
+ except pythoncom.com_error:
+ pass
+ print('\tCLSID = CLSID_Sink = ' + repr(self.clsid), file=stream)
+ if self.coclass_clsid is None:
+ print("\tcoclass_clsid = None", file=stream)
+ else:
+ print("\tcoclass_clsid = " + repr(self.coclass_clsid), file=stream)
+ print('\t_public_methods_ = [] # For COM Server support', file=stream)
+ WriteSinkEventMap(self, stream)
+ print(file=stream)
+ print('\tdef __init__(self, oobj = None):', file=stream)
+ print("\t\tif oobj is None:", file=stream)
+ print("\t\t\tself._olecp = None", file=stream)
+ print("\t\telse:", file=stream)
+ print('\t\t\timport win32com.server.util', file=stream)
+ print('\t\t\tfrom win32com.server.policy import EventHandlerPolicy', file=stream)
+ print('\t\t\tcpc=oobj._oleobj_.QueryInterface(pythoncom.IID_IConnectionPointContainer)', file=stream)
+ print('\t\t\tcp=cpc.FindConnectionPoint(self.CLSID_Sink)', file=stream)
+ print('\t\t\tcookie=cp.Advise(win32com.server.util.wrap(self, usePolicy=EventHandlerPolicy))', file=stream)
+ print('\t\t\tself._olecp,self._olecp_cookie = cp,cookie', file=stream)
+ print('\tdef __del__(self):', file=stream)
+ print('\t\ttry:', file=stream)
+ print('\t\t\tself.close()', file=stream)
+ print('\t\texcept pythoncom.com_error:', file=stream)
+ print('\t\t\tpass', file=stream)
+ print('\tdef close(self):', file=stream)
+ print('\t\tif self._olecp is not None:', file=stream)
+ print('\t\t\tcp,cookie,self._olecp,self._olecp_cookie = self._olecp,self._olecp_cookie,None,None', file=stream)
+ print('\t\t\tcp.Unadvise(cookie)', file=stream)
+ print('\tdef _query_interface_(self, iid):', file=stream)
+ print('\t\timport win32com.server.util', file=stream)
+ print('\t\tif iid==self.CLSID_Sink: return win32com.server.util.wrap(self)', file=stream)
+ print(file=stream)
+ self.bWritten = 1
+
+ def WriteCallbackClassBody(self, generator):
+ stream = generator.file
+ print("\t# Event Handlers", file=stream)
+ print("\t# If you create handlers, they should have the following prototypes:", file=stream)
+ for name, entry in list(self.propMapGet.items()) + list(self.propMapPut.items()) + list(self.mapFuncs.items()):
+ fdesc = entry.desc
+ methName = MakeEventMethodName(entry.names[0])
+ print('#\tdef ' + methName + '(self' + build.BuildCallList(fdesc, entry.names, "defaultNamedOptArg", "defaultNamedNotOptArg","defaultUnnamedArg", "pythoncom.Missing", is_comment = True) + '):', file=stream)
+ if entry.doc and entry.doc[1]:
+ print('#\t\t' + build._makeDocString(entry.doc[1]), file=stream)
+ print(file=stream)
+ self.bWritten = 1
+
+ def WriteClassBody(self, generator):
+ stream = generator.file
+ # Write in alpha order.
+ names = list(self.mapFuncs.keys())
+ names.sort()
+ specialItems = {"count":None, "item":None,"value":None,"_newenum":None} # If found, will end up with (entry, invoke_tupe)
+ itemCount = None
+ for name in names:
+ entry=self.mapFuncs[name]
+ # skip [restricted] methods, unless it is the
+ # enumerator (which, being part of the "system",
+ # we know about and can use)
+ dispid = entry.desc[0]
+ if entry.desc[9] & pythoncom.FUNCFLAG_FRESTRICTED and \
+ dispid != pythoncom.DISPID_NEWENUM:
+ continue
+ # If not accessible via IDispatch, then we can't use it here.
+ if entry.desc[3] != pythoncom.FUNC_DISPATCH:
+ continue
+ if dispid==pythoncom.DISPID_VALUE:
+ lkey = "value"
+ elif dispid==pythoncom.DISPID_NEWENUM:
+ specialItems["_newenum"] = (entry, entry.desc[4], None)
+ continue # Dont build this one now!
+ else:
+ lkey = name.lower()
+ if lkey in specialItems and specialItems[lkey] is None: # remember if a special one.
+ specialItems[lkey] = (entry, entry.desc[4], None)
+ if generator.bBuildHidden or not entry.hidden:
+ if entry.GetResultName():
+ print('\t# Result is of type ' + entry.GetResultName(), file=stream)
+ if entry.wasProperty:
+ print('\t# The method %s is actually a property, but must be used as a method to correctly pass the arguments' % name, file=stream)
+ ret = self.MakeFuncMethod(entry,build.MakePublicAttributeName(name))
+ for line in ret:
+ print(line, file=stream)
+ print("\t_prop_map_get_ = {", file=stream)
+ names = list(self.propMap.keys()); names.sort()
+ for key in names:
+ entry = self.propMap[key]
+ if generator.bBuildHidden or not entry.hidden:
+ resultName = entry.GetResultName()
+ if resultName:
+ print("\t\t# Property '%s' is an object of type '%s'" % (key, resultName), file=stream)
+ lkey = key.lower()
+ details = entry.desc
+ resultDesc = details[2]
+ argDesc = ()
+ mapEntry = MakeMapLineEntry(details[0], pythoncom.DISPATCH_PROPERTYGET, resultDesc, argDesc, key, entry.GetResultCLSIDStr())
+
+ if entry.desc[0]==pythoncom.DISPID_VALUE:
+ lkey = "value"
+ elif entry.desc[0]==pythoncom.DISPID_NEWENUM:
+ lkey = "_newenum"
+ else:
+ lkey = key.lower()
+ if lkey in specialItems and specialItems[lkey] is None: # remember if a special one.
+ specialItems[lkey] = (entry, pythoncom.DISPATCH_PROPERTYGET, mapEntry)
+ # All special methods, except _newenum, are written
+ # "normally". This is a mess!
+ if entry.desc[0]==pythoncom.DISPID_NEWENUM:
+ continue
+
+ print('\t\t"%s": %s,' % (build.MakePublicAttributeName(key), mapEntry), file=stream)
+ names = list(self.propMapGet.keys()); names.sort()
+ for key in names:
+ entry = self.propMapGet[key]
+ if generator.bBuildHidden or not entry.hidden:
+ if entry.GetResultName():
+ print("\t\t# Method '%s' returns object of type '%s'" % (key, entry.GetResultName()), file=stream)
+ details = entry.desc
+ lkey = key.lower()
+ argDesc = details[2]
+ resultDesc = details[8]
+ mapEntry = MakeMapLineEntry(details[0], pythoncom.DISPATCH_PROPERTYGET, resultDesc, argDesc, key, entry.GetResultCLSIDStr())
+ if entry.desc[0]==pythoncom.DISPID_VALUE:
+ lkey = "value"
+ elif entry.desc[0]==pythoncom.DISPID_NEWENUM:
+ lkey = "_newenum"
+ else:
+ lkey = key.lower()
+ if lkey in specialItems and specialItems[lkey] is None: # remember if a special one.
+ specialItems[lkey]=(entry, pythoncom.DISPATCH_PROPERTYGET, mapEntry)
+ # All special methods, except _newenum, are written
+ # "normally". This is a mess!
+ if entry.desc[0]==pythoncom.DISPID_NEWENUM:
+ continue
+ print('\t\t"%s": %s,' % (build.MakePublicAttributeName(key), mapEntry), file=stream)
+
+ print("\t}", file=stream)
+
+ print("\t_prop_map_put_ = {", file=stream)
+ # These are "Invoke" args
+ names = list(self.propMap.keys()); names.sort()
+ for key in names:
+ entry = self.propMap[key]
+ if generator.bBuildHidden or not entry.hidden:
+ lkey=key.lower()
+ details = entry.desc
+ # If default arg is None, write an empty tuple
+ defArgDesc = build.MakeDefaultArgRepr(details[2])
+ if defArgDesc is None:
+ defArgDesc = ""
+ else:
+ defArgDesc = defArgDesc + ","
+ print('\t\t"%s" : ((%s, LCID, %d, 0),(%s)),' % (build.MakePublicAttributeName(key), details[0], pythoncom.DISPATCH_PROPERTYPUT, defArgDesc), file=stream)
+
+ names = list(self.propMapPut.keys()); names.sort()
+ for key in names:
+ entry = self.propMapPut[key]
+ if generator.bBuildHidden or not entry.hidden:
+ details = entry.desc
+ defArgDesc = MakeDefaultArgsForPropertyPut(details[2])
+ print('\t\t"%s": ((%s, LCID, %d, 0),%s),' % (build.MakePublicAttributeName(key), details[0], details[4], defArgDesc), file=stream)
+ print("\t}", file=stream)
+
+ if specialItems["value"]:
+ entry, invoketype, propArgs = specialItems["value"]
+ if propArgs is None:
+ typename = "method"
+ ret = self.MakeFuncMethod(entry,'__call__')
+ else:
+ typename = "property"
+ ret = [ "\tdef __call__(self):\n\t\treturn self._ApplyTypes_(*%s)" % propArgs]
+ print("\t# Default %s for this class is '%s'" % (typename, entry.names[0]), file=stream)
+ for line in ret:
+ print(line, file=stream)
+ if sys.version_info > (3,0):
+ print("\tdef __str__(self, *args):", file=stream)
+ print("\t\treturn str(self.__call__(*args))", file=stream)
+ else:
+ print("\tdef __unicode__(self, *args):", file=stream)
+ print("\t\ttry:", file=stream)
+ print("\t\t\treturn unicode(self.__call__(*args))", file=stream)
+ print("\t\texcept pythoncom.com_error:", file=stream)
+ print("\t\t\treturn repr(self)", file=stream)
+ print("\tdef __str__(self, *args):", file=stream)
+ print("\t\treturn str(self.__unicode__(*args))", file=stream)
+ print("\tdef __int__(self, *args):", file=stream)
+ print("\t\treturn int(self.__call__(*args))", file=stream)
+
+ # _NewEnum (DISPID_NEWENUM) does not appear in typelib for many office objects,
+ # but it can still be retrieved at runtime, so always create __iter__.
+ # Also, some of those same objects use 1-based indexing, causing the old-style
+ # __getitem__ iteration to fail for index 0 where the dynamic iteration succeeds.
+ if specialItems["_newenum"]:
+ enumEntry, invoketype, propArgs = specialItems["_newenum"]
+ invkind = enumEntry.desc[4]
+ # ??? Wouldn't this be the resultCLSID for the iterator itself, rather than the resultCLSID
+ # for the result of each Next() call, which is what it's used for ???
+ resultCLSID = enumEntry.GetResultCLSIDStr()
+ else:
+ invkind = pythoncom.DISPATCH_METHOD | pythoncom.DISPATCH_PROPERTYGET
+ resultCLSID = "None"
+ # If we dont have a good CLSID for the enum result, assume it is the same as the Item() method.
+ if resultCLSID == "None" and "Item" in self.mapFuncs:
+ resultCLSID = self.mapFuncs["Item"].GetResultCLSIDStr()
+ print('\tdef __iter__(self):', file=stream)
+ print('\t\t"Return a Python iterator for this object"', file=stream)
+ print('\t\ttry:', file=stream)
+ print('\t\t\tob = self._oleobj_.InvokeTypes(%d,LCID,%d,(13, 10),())' % (pythoncom.DISPID_NEWENUM, invkind), file=stream)
+ print('\t\texcept pythoncom.error:', file=stream)
+ print('\t\t\traise TypeError("This object does not support enumeration")', file=stream)
+ # Iterator is wrapped as PyIEnumVariant, and each result of __next__ is Dispatch'ed if necessary
+ print('\t\treturn win32com.client.util.Iterator(ob, %s)' %resultCLSID, file=stream)
+
+ if specialItems["item"]:
+ entry, invoketype, propArgs = specialItems["item"]
+ resultCLSID = entry.GetResultCLSIDStr()
+ print('\t#This class has Item property/method which allows indexed access with the object[key] syntax.', file=stream)
+ print('\t#Some objects will accept a string or other type of key in addition to integers.', file=stream)
+ print('\t#Note that many Office objects do not use zero-based indexing.', file=stream)
+ print('\tdef __getitem__(self, key):', file=stream)
+ print('\t\treturn self._get_good_object_(self._oleobj_.Invoke(*(%d, LCID, %d, 1, key)), "Item", %s)' \
+ % (entry.desc[0], invoketype, resultCLSID), file=stream)
+
+ if specialItems["count"]:
+ entry, invoketype, propArgs = specialItems["count"]
+ if propArgs is None:
+ typename = "method"
+ ret = self.MakeFuncMethod(entry,'__len__')
+ else:
+ typename = "property"
+ ret = [ "\tdef __len__(self):\n\t\treturn self._ApplyTypes_(*%s)" % propArgs]
+ print("\t#This class has Count() %s - allow len(ob) to provide this" % (typename), file=stream)
+ for line in ret:
+ print(line, file=stream)
+ # Also include a __nonzero__
+ print("\t#This class has a __len__ - this is needed so 'if object:' always returns TRUE.", file=stream)
+ print("\tdef __nonzero__(self):", file=stream)
+ print("\t\treturn True", file=stream)
+
+class CoClassItem(build.OleItem, WritableItem):
+ order = 5
+ typename = "COCLASS"
+
+ def __init__(self, typeinfo, attr, doc=None, sources = [], interfaces = [], bForUser=1):
+ build.OleItem.__init__(self, doc)
+ self.clsid = attr[0]
+ self.sources = sources
+ self.interfaces = interfaces
+ self.bIsDispatch = 1 # Pretend it is so it is written to the class map.
+
+ def WriteClass(self, generator):
+ generator.checkWriteCoClassBaseClass()
+ doc = self.doc
+ stream = generator.file
+ if generator.generate_type == GEN_DEMAND_CHILD:
+ # Some special imports we must setup.
+ referenced_items = []
+ for ref, flag in self.sources:
+ referenced_items.append(ref)
+ for ref, flag in self.interfaces:
+ referenced_items.append(ref)
+ print("import sys", file=stream)
+ for ref in referenced_items:
+ print("__import__('%s.%s')" % (generator.base_mod_name, ref.python_name), file=stream)
+ print("%s = sys.modules['%s.%s'].%s" % (ref.python_name, generator.base_mod_name, ref.python_name, ref.python_name), file=stream)
+ # And pretend we have written it - the name is now available as if we had!
+ ref.bWritten = 1
+ try:
+ progId = pythoncom.ProgIDFromCLSID(self.clsid)
+ print("# This CoClass is known by the name '%s'" % (progId), file=stream)
+ except pythoncom.com_error:
+ pass
+ print('class %s(CoClassBaseClass): # A CoClass' % (self.python_name), file=stream)
+ if doc and doc[1]: print('\t# ' + doc[1], file=stream)
+ print('\tCLSID = %r' % (self.clsid,), file=stream)
+ print('\tcoclass_sources = [', file=stream)
+ defItem = None
+ for item, flag in self.sources:
+ if flag & pythoncom.IMPLTYPEFLAG_FDEFAULT:
+ defItem = item
+ # If we have written a Python class, reference the name -
+ # otherwise just the IID.
+ if item.bWritten: key = item.python_name
+ else: key = repr(str(item.clsid)) # really the iid.
+ print('\t\t%s,' % (key), file=stream)
+ print('\t]', file=stream)
+ if defItem:
+ if defItem.bWritten: defName = defItem.python_name
+ else: defName = repr(str(defItem.clsid)) # really the iid.
+ print('\tdefault_source = %s' % (defName,), file=stream)
+ print('\tcoclass_interfaces = [', file=stream)
+ defItem = None
+ for item, flag in self.interfaces:
+ if flag & pythoncom.IMPLTYPEFLAG_FDEFAULT: # and dual:
+ defItem = item
+ # If we have written a class, refeence its name, otherwise the IID
+ if item.bWritten: key = item.python_name
+ else: key = repr(str(item.clsid)) # really the iid.
+ print('\t\t%s,' % (key,), file=stream)
+ print('\t]', file=stream)
+ if defItem:
+ if defItem.bWritten: defName = defItem.python_name
+ else: defName = repr(str(defItem.clsid)) # really the iid.
+ print('\tdefault_interface = %s' % (defName,), file=stream)
+ self.bWritten = 1
+ print(file=stream)
+
+class GeneratorProgress:
+ def __init__(self):
+ pass
+ def Starting(self, tlb_desc):
+ """Called when the process starts.
+ """
+ self.tlb_desc = tlb_desc
+ def Finished(self):
+ """Called when the process is complete.
+ """
+ def SetDescription(self, desc, maxticks = None):
+ """We are entering a major step. If maxticks, then this
+ is how many ticks we expect to make until finished
+ """
+ def Tick(self, desc = None):
+ """Minor progress step. Can provide new description if necessary
+ """
+ def VerboseProgress(self, desc):
+ """Verbose/Debugging output.
+ """
+ def LogWarning(self, desc):
+ """If a warning is generated
+ """
+ def LogBeginGenerate(self, filename):
+ pass
+ def Close(self):
+ pass
+
+class Generator:
+ def __init__(self, typelib, sourceFilename, progressObject, bBuildHidden=1, bUnicodeToString=None):
+ assert bUnicodeToString is None, "this is deprecated and will go away"
+ self.bHaveWrittenDispatchBaseClass = 0
+ self.bHaveWrittenCoClassBaseClass = 0
+ self.bHaveWrittenEventBaseClass = 0
+ self.typelib = typelib
+ self.sourceFilename = sourceFilename
+ self.bBuildHidden = bBuildHidden
+ self.progress = progressObject
+ # These 2 are later additions and most of the code still 'print's...
+ self.file = None
+
+ def CollectOleItemInfosFromType(self):
+ ret = []
+ for i in range(self.typelib.GetTypeInfoCount()):
+ info = self.typelib.GetTypeInfo(i)
+ infotype = self.typelib.GetTypeInfoType(i)
+ doc = self.typelib.GetDocumentation(i)
+ attr = info.GetTypeAttr()
+ ret.append((info, infotype, doc, attr))
+ return ret
+
+ def _Build_CoClass(self, type_info_tuple):
+ info, infotype, doc, attr = type_info_tuple
+ # find the source and dispinterfaces for the coclass
+ child_infos = []
+ for j in range(attr[8]):
+ flags = info.GetImplTypeFlags(j)
+ try:
+ refType = info.GetRefTypeInfo(info.GetRefTypeOfImplType(j))
+ except pythoncom.com_error:
+ # Can't load a dependent typelib?
+ continue
+ refAttr = refType.GetTypeAttr()
+ child_infos.append( (info, refAttr.typekind, refType, refType.GetDocumentation(-1), refAttr, flags) )
+
+ # Done generating children - now the CoClass itself.
+ newItem = CoClassItem(info, attr, doc)
+ return newItem, child_infos
+
+ def _Build_CoClassChildren(self, coclass, coclass_info, oleItems, vtableItems):
+ sources = {}
+ interfaces = {}
+ for info, info_type, refType, doc, refAttr, flags in coclass_info:
+# sys.stderr.write("Attr typeflags for coclass referenced object %s=%d (%d), typekind=%d\n" % (name, refAttr.wTypeFlags, refAttr.wTypeFlags & pythoncom.TYPEFLAG_FDUAL,refAttr.typekind))
+ if refAttr.typekind == pythoncom.TKIND_DISPATCH or \
+ (refAttr.typekind == pythoncom.TKIND_INTERFACE and refAttr[11] & pythoncom.TYPEFLAG_FDISPATCHABLE):
+ clsid = refAttr[0]
+ if clsid in oleItems:
+ dispItem = oleItems[clsid]
+ else:
+ dispItem = DispatchItem(refType, refAttr, doc)
+ oleItems[dispItem.clsid] = dispItem
+ dispItem.coclass_clsid = coclass.clsid
+ if flags & pythoncom.IMPLTYPEFLAG_FSOURCE:
+ dispItem.bIsSink = 1
+ sources[dispItem.clsid] = (dispItem, flags)
+ else:
+ interfaces[dispItem.clsid] = (dispItem, flags)
+ # If dual interface, make do that too.
+ if clsid not in vtableItems and refAttr[11] & pythoncom.TYPEFLAG_FDUAL:
+ refType = refType.GetRefTypeInfo(refType.GetRefTypeOfImplType(-1))
+ refAttr = refType.GetTypeAttr()
+ assert refAttr.typekind == pythoncom.TKIND_INTERFACE, "must be interface bynow!"
+ vtableItem = VTableItem(refType, refAttr, doc)
+ vtableItems[clsid] = vtableItem
+ coclass.sources = list(sources.values())
+ coclass.interfaces = list(interfaces.values())
+
+ def _Build_Interface(self, type_info_tuple):
+ info, infotype, doc, attr = type_info_tuple
+ oleItem = vtableItem = None
+ if infotype == pythoncom.TKIND_DISPATCH or \
+ (infotype == pythoncom.TKIND_INTERFACE and attr[11] & pythoncom.TYPEFLAG_FDISPATCHABLE):
+ oleItem = DispatchItem(info, attr, doc)
+ # If this DISPATCH interface dual, then build that too.
+ if (attr.wTypeFlags & pythoncom.TYPEFLAG_FDUAL):
+ # Get the vtable interface
+ refhtype = info.GetRefTypeOfImplType(-1)
+ info = info.GetRefTypeInfo(refhtype)
+ attr = info.GetTypeAttr()
+ infotype = pythoncom.TKIND_INTERFACE
+ else:
+ infotype = None
+ assert infotype in [None, pythoncom.TKIND_INTERFACE], "Must be a real interface at this point"
+ if infotype == pythoncom.TKIND_INTERFACE:
+ vtableItem = VTableItem(info, attr, doc)
+ return oleItem, vtableItem
+
+ def BuildOleItemsFromType(self):
+ assert self.bBuildHidden, "This code doesnt look at the hidden flag - I thought everyone set it true!?!?!"
+ oleItems = {}
+ enumItems = {}
+ recordItems = {}
+ vtableItems = {}
+
+ for type_info_tuple in self.CollectOleItemInfosFromType():
+ info, infotype, doc, attr = type_info_tuple
+ clsid = attr[0]
+ if infotype == pythoncom.TKIND_ENUM or infotype == pythoncom.TKIND_MODULE:
+ newItem = EnumerationItem(info, attr, doc)
+ enumItems[newItem.doc[0]] = newItem
+ # We never hide interfaces (MSAccess, for example, nominates interfaces as
+ # hidden, assuming that you only ever use them via the CoClass)
+ elif infotype in [pythoncom.TKIND_DISPATCH, pythoncom.TKIND_INTERFACE]:
+ if clsid not in oleItems:
+ oleItem, vtableItem = self._Build_Interface(type_info_tuple)
+ oleItems[clsid] = oleItem # Even "None" goes in here.
+ if vtableItem is not None:
+ vtableItems[clsid] = vtableItem
+ elif infotype == pythoncom.TKIND_RECORD or infotype == pythoncom.TKIND_UNION:
+ newItem = RecordItem(info, attr, doc)
+ recordItems[newItem.clsid] = newItem
+ elif infotype == pythoncom.TKIND_ALIAS:
+ # We dont care about alias' - handled intrinsicly.
+ continue
+ elif infotype == pythoncom.TKIND_COCLASS:
+ newItem, child_infos = self._Build_CoClass(type_info_tuple)
+ self._Build_CoClassChildren(newItem, child_infos, oleItems, vtableItems)
+ oleItems[newItem.clsid] = newItem
+ else:
+ self.progress.LogWarning("Unknown TKIND found: %d" % infotype)
+
+ return oleItems, enumItems, recordItems, vtableItems
+
+ def open_writer(self, filename, encoding="mbcs"):
+ # A place to put code to open a file with the appropriate encoding.
+ # Does *not* set self.file - just opens and returns a file.
+ # Actually *deletes* the filename asked for and returns a handle to a
+ # temp file - finish_writer then puts everything back in place. This
+ # is so errors don't leave a 1/2 generated file around causing bizarre
+ # errors later.
+ # Could be a classmethod one day...
+ try:
+ os.unlink(filename)
+ except os.error:
+ pass
+ filename = filename + ".temp"
+ if sys.version_info > (3,0):
+ ret = open(filename, "wt", encoding=encoding)
+ else:
+ import codecs # not available in py3k.
+ ret = codecs.open(filename, "w", encoding)
+ return ret
+
+ def finish_writer(self, filename, f, worked):
+ f.close()
+ if worked:
+ os.rename(filename + ".temp", filename)
+ else:
+ os.unlink(filename + ".temp")
+
+ def generate(self, file, is_for_demand = 0):
+ if is_for_demand:
+ self.generate_type = GEN_DEMAND_BASE
+ else:
+ self.generate_type = GEN_FULL
+ self.file = file
+ self.do_generate()
+ self.file = None
+ self.progress.Finished()
+
+ def do_gen_file_header(self):
+ la = self.typelib.GetLibAttr()
+ moduleDoc = self.typelib.GetDocumentation(-1)
+ docDesc = ""
+ if moduleDoc[1]:
+ docDesc = moduleDoc[1]
+
+ # Reset all the 'per file' state
+ self.bHaveWrittenDispatchBaseClass = 0
+ self.bHaveWrittenCoClassBaseClass = 0
+ self.bHaveWrittenEventBaseClass = 0
+ # You must provide a file correctly configured for writing unicode.
+ # We assert this is it may indicate somewhere in pywin32 that needs
+ # upgrading.
+ assert self.file.encoding, self.file
+ encoding = self.file.encoding # or "mbcs"
+
+ print('# -*- coding: %s -*-' % (encoding,), file=self.file)
+ print('# Created by makepy.py version %s' % (makepy_version,), file=self.file)
+ print('# By python version %s' % \
+ (sys.version.replace("\n", "-"),), file=self.file)
+ if self.sourceFilename:
+ print("# From type library '%s'" % (os.path.split(self.sourceFilename)[1],), file=self.file)
+ print('# On %s' % time.ctime(time.time()), file=self.file)
+
+ print(build._makeDocString(docDesc), file=self.file)
+
+ print('makepy_version =', repr(makepy_version), file=self.file)
+ print('python_version = 0x%x' % (sys.hexversion,), file=self.file)
+ print(file=self.file)
+ print('import win32com.client.CLSIDToClass, pythoncom, pywintypes', file=self.file)
+ print('import win32com.client.util', file=self.file)
+ print('from pywintypes import IID', file=self.file)
+ print('from win32com.client import Dispatch', file=self.file)
+ print(file=self.file)
+ print('# The following 3 lines may need tweaking for the particular server', file=self.file)
+ print('# Candidates are pythoncom.Missing, .Empty and .ArgNotFound', file=self.file)
+ print('defaultNamedOptArg=pythoncom.Empty', file=self.file)
+ print('defaultNamedNotOptArg=pythoncom.Empty', file=self.file)
+ print('defaultUnnamedArg=pythoncom.Empty', file=self.file)
+ print(file=self.file)
+ print('CLSID = ' + repr(la[0]), file=self.file)
+ print('MajorVersion = ' + str(la[3]), file=self.file)
+ print('MinorVersion = ' + str(la[4]), file=self.file)
+ print('LibraryFlags = ' + str(la[5]), file=self.file)
+ print('LCID = ' + hex(la[1]), file=self.file)
+ print(file=self.file)
+
+ def do_generate(self):
+ moduleDoc = self.typelib.GetDocumentation(-1)
+ stream = self.file
+ docDesc = ""
+ if moduleDoc[1]:
+ docDesc = moduleDoc[1]
+ self.progress.Starting(docDesc)
+ self.progress.SetDescription("Building definitions from type library...")
+
+ self.do_gen_file_header()
+
+ oleItems, enumItems, recordItems, vtableItems = self.BuildOleItemsFromType()
+
+ self.progress.SetDescription("Generating...", len(oleItems)+len(enumItems)+len(vtableItems))
+
+ # Generate the constants and their support.
+ if enumItems:
+ print("class constants:", file=stream)
+ items = list(enumItems.values())
+ items.sort()
+ num_written = 0
+ for oleitem in items:
+ num_written += oleitem.WriteEnumerationItems(stream)
+ self.progress.Tick()
+ if not num_written:
+ print("\tpass", file=stream)
+ print(file=stream)
+
+ if self.generate_type == GEN_FULL:
+ items = [l for l in oleItems.values() if l is not None]
+ items.sort()
+ for oleitem in items:
+ self.progress.Tick()
+ oleitem.WriteClass(self)
+
+ items = list(vtableItems.values())
+ items.sort()
+ for oleitem in items:
+ self.progress.Tick()
+ oleitem.WriteClass(self)
+ else:
+ self.progress.Tick(len(oleItems)+len(vtableItems))
+
+ print('RecordMap = {', file=stream)
+ for record in recordItems.values():
+ if record.clsid == pythoncom.IID_NULL:
+ print("\t###%s: %s, # Record disabled because it doesn't have a non-null GUID" % (repr(record.doc[0]), repr(str(record.clsid))), file=stream)
+ else:
+ print("\t%s: %s," % (repr(record.doc[0]), repr(str(record.clsid))), file=stream)
+ print("}", file=stream)
+ print(file=stream)
+
+ # Write out _all_ my generated CLSID's in the map
+ if self.generate_type == GEN_FULL:
+ print('CLSIDToClassMap = {', file=stream)
+ for item in oleItems.values():
+ if item is not None and item.bWritten:
+ print("\t'%s' : %s," % (str(item.clsid), item.python_name), file=stream)
+ print('}', file=stream)
+ print('CLSIDToPackageMap = {}', file=stream)
+ print('win32com.client.CLSIDToClass.RegisterCLSIDsFromDict( CLSIDToClassMap )', file=stream)
+ print("VTablesToPackageMap = {}", file=stream)
+ print("VTablesToClassMap = {", file=stream)
+ for item in vtableItems.values():
+ print("\t'%s' : '%s'," % (item.clsid,item.python_name), file=stream)
+ print('}', file=stream)
+ print(file=stream)
+
+ else:
+ print('CLSIDToClassMap = {}', file=stream)
+ print('CLSIDToPackageMap = {', file=stream)
+ for item in oleItems.values():
+ if item is not None:
+ print("\t'%s' : %s," % (str(item.clsid), repr(item.python_name)), file=stream)
+ print('}', file=stream)
+ print("VTablesToClassMap = {}", file=stream)
+ print("VTablesToPackageMap = {", file=stream)
+ for item in vtableItems.values():
+ print("\t'%s' : '%s'," % (item.clsid,item.python_name), file=stream)
+ print('}', file=stream)
+ print(file=stream)
+
+ print(file=stream)
+ # Bit of a hack - build a temp map of iteItems + vtableItems - coClasses
+ map = {}
+ for item in oleItems.values():
+ if item is not None and not isinstance(item, CoClassItem):
+ map[item.python_name] = item.clsid
+ for item in vtableItems.values(): # No nones or CoClasses in this map
+ map[item.python_name] = item.clsid
+
+ print("NamesToIIDMap = {", file=stream)
+ for name, iid in map.items():
+ print("\t'%s' : '%s'," % (name, iid), file=stream)
+ print('}', file=stream)
+ print(file=stream)
+
+ if enumItems:
+ print('win32com.client.constants.__dicts__.append(constants.__dict__)', file=stream)
+ print(file=stream)
+
+ def generate_child(self, child, dir):
+ "Generate a single child. May force a few children to be built as we generate deps"
+ self.generate_type = GEN_DEMAND_CHILD
+
+ la = self.typelib.GetLibAttr()
+ lcid = la[1]
+ clsid = la[0]
+ major=la[3]
+ minor=la[4]
+ self.base_mod_name = "win32com.gen_py." + str(clsid)[1:-1] + "x%sx%sx%s" % (lcid, major, minor)
+ try:
+ # Process the type library's CoClass objects, looking for the
+ # specified name, or where a child has the specified name.
+ # This ensures that all interesting things (including event interfaces)
+ # are generated correctly.
+ oleItems = {}
+ vtableItems = {}
+ infos = self.CollectOleItemInfosFromType()
+ found = 0
+ for type_info_tuple in infos:
+ info, infotype, doc, attr = type_info_tuple
+ if infotype == pythoncom.TKIND_COCLASS:
+ coClassItem, child_infos = self._Build_CoClass(type_info_tuple)
+ found = build.MakePublicAttributeName(doc[0])==child
+ if not found:
+ # OK, check the child interfaces
+ for info, info_type, refType, doc, refAttr, flags in child_infos:
+ if build.MakePublicAttributeName(doc[0]) == child:
+ found = 1
+ break
+ if found:
+ oleItems[coClassItem.clsid] = coClassItem
+ self._Build_CoClassChildren(coClassItem, child_infos, oleItems, vtableItems)
+ break
+ if not found:
+ # Doesn't appear in a class defn - look in the interface objects for it
+ for type_info_tuple in infos:
+ info, infotype, doc, attr = type_info_tuple
+ if infotype in [pythoncom.TKIND_INTERFACE, pythoncom.TKIND_DISPATCH]:
+ if build.MakePublicAttributeName(doc[0]) == child:
+ found = 1
+ oleItem, vtableItem = self._Build_Interface(type_info_tuple)
+ oleItems[clsid] = oleItem # Even "None" goes in here.
+ if vtableItem is not None:
+ vtableItems[clsid] = vtableItem
+
+ assert found, "Cant find the '%s' interface in the CoClasses, or the interfaces" % (child,)
+ # Make a map of iid: dispitem, vtableitem)
+ items = {}
+ for key, value in oleItems.items():
+ items[key] = (value,None)
+ for key, value in vtableItems.items():
+ existing = items.get(key, None)
+ if existing is not None:
+ new_val = existing[0], value
+ else:
+ new_val = None, value
+ items[key] = new_val
+
+ self.progress.SetDescription("Generating...", len(items))
+ for oleitem, vtableitem in items.values():
+ an_item = oleitem or vtableitem
+ assert not self.file, "already have a file?"
+ # like makepy.py, we gen to a .temp file so failure doesn't
+ # leave a 1/2 generated mess.
+ out_name = os.path.join(dir, an_item.python_name) + ".py"
+ worked = False
+ self.file = self.open_writer(out_name)
+ try:
+ if oleitem is not None:
+ self.do_gen_child_item(oleitem)
+ if vtableitem is not None:
+ self.do_gen_child_item(vtableitem)
+ self.progress.Tick()
+ worked = True
+ finally:
+ self.finish_writer(out_name, self.file, worked)
+ self.file = None
+ finally:
+ self.progress.Finished()
+
+ def do_gen_child_item(self, oleitem):
+ moduleDoc = self.typelib.GetDocumentation(-1)
+ docDesc = ""
+ if moduleDoc[1]:
+ docDesc = moduleDoc[1]
+ self.progress.Starting(docDesc)
+ self.progress.SetDescription("Building definitions from type library...")
+ self.do_gen_file_header()
+ oleitem.WriteClass(self)
+ if oleitem.bWritten:
+ print('win32com.client.CLSIDToClass.RegisterCLSID( "%s", %s )' % (oleitem.clsid, oleitem.python_name), file=self.file)
+
+ def checkWriteDispatchBaseClass(self):
+ if not self.bHaveWrittenDispatchBaseClass:
+ print("from win32com.client import DispatchBaseClass", file=self.file)
+ self.bHaveWrittenDispatchBaseClass = 1
+
+ def checkWriteCoClassBaseClass(self):
+ if not self.bHaveWrittenCoClassBaseClass:
+ print("from win32com.client import CoClassBaseClass", file=self.file)
+ self.bHaveWrittenCoClassBaseClass = 1
+
+ def checkWriteEventBaseClass(self):
+ # Not a base class as such...
+ if not self.bHaveWrittenEventBaseClass:
+ # Nothing to do any more!
+ self.bHaveWrittenEventBaseClass = 1
+
+if __name__=='__main__':
+ print("This is a worker module. Please use makepy to generate Python files.")
diff --git a/Lib/site-packages/win32com/client/makepy.py b/Lib/site-packages/win32com/client/makepy.py
new file mode 100644
index 0000000..df6f3f6
--- /dev/null
+++ b/Lib/site-packages/win32com/client/makepy.py
@@ -0,0 +1,390 @@
+# Originally written by Curt Hagenlocher, and various bits
+# and pieces by Mark Hammond (and now Greg Stein has had
+# a go too :-)
+
+# Note that the main worker code has been moved to genpy.py
+# As this is normally run from the command line, it reparses the code each time.
+# Now this is nothing more than the command line handler and public interface.
+
+# XXX - TO DO
+# XXX - Greg and Mark have some ideas for a revamp - just no
+# time - if you want to help, contact us for details.
+# Main idea is to drop the classes exported and move to a more
+# traditional data driven model.
+
+"""Generate a .py file from an OLE TypeLibrary file.
+
+
+ This module is concerned only with the actual writing of
+ a .py file. It draws on the @build@ module, which builds
+ the knowledge of a COM interface.
+
+"""
+usageHelp = """ \
+
+Usage:
+
+ makepy.py [-i] [-v|q] [-h] [-u] [-o output_file] [-d] [typelib, ...]
+
+ -i -- Show information for the specified typelib.
+
+ -v -- Verbose output.
+
+ -q -- Quiet output.
+
+ -h -- Do not generate hidden methods.
+
+ -u -- Python 1.5 and earlier: Do NOT convert all Unicode objects to
+ strings.
+
+ Python 1.6 and later: Convert all Unicode objects to strings.
+
+ -o -- Create output in a specified output file. If the path leading
+ to the file does not exist, any missing directories will be
+ created.
+ NOTE: -o cannot be used with -d. This will generate an error.
+
+ -d -- Generate the base code now and the class code on demand.
+ Recommended for large type libraries.
+
+ typelib -- A TLB, DLL, OCX or anything containing COM type information.
+ If a typelib is not specified, a window containing a textbox
+ will open from which you can select a registered type
+ library.
+
+Examples:
+
+ makepy.py -d
+
+ Presents a list of registered type libraries from which you can make
+ a selection.
+
+ makepy.py -d "Microsoft Excel 8.0 Object Library"
+
+ Generate support for the type library with the specified description
+ (in this case, the MS Excel object model).
+
+"""
+
+import sys, os, pythoncom
+from win32com.client import genpy, selecttlb, gencache
+from win32com.client import Dispatch
+
+bForDemandDefault = 0 # Default value of bForDemand - toggle this to change the world - see also gencache.py
+
+error = "makepy.error"
+
+def usage():
+ sys.stderr.write (usageHelp)
+ sys.exit(2)
+
+def ShowInfo(spec):
+ if not spec:
+ tlbSpec = selecttlb.SelectTlb(excludeFlags=selecttlb.FLAG_HIDDEN)
+ if tlbSpec is None:
+ return
+ try:
+ tlb = pythoncom.LoadRegTypeLib(tlbSpec.clsid, tlbSpec.major, tlbSpec.minor, tlbSpec.lcid)
+ except pythoncom.com_error: # May be badly registered.
+ sys.stderr.write("Warning - could not load registered typelib '%s'\n" % (tlbSpec.clsid))
+ tlb = None
+
+ infos = [(tlb, tlbSpec)]
+ else:
+ infos = GetTypeLibsForSpec(spec)
+ for (tlb, tlbSpec) in infos:
+ desc = tlbSpec.desc
+ if desc is None:
+ if tlb is None:
+ desc = "" % (tlbSpec.dll)
+ else:
+ desc = tlb.GetDocumentation(-1)[0]
+ print(desc)
+ print(" %s, lcid=%s, major=%s, minor=%s" % (tlbSpec.clsid, tlbSpec.lcid, tlbSpec.major, tlbSpec.minor))
+ print(" >>> # Use these commands in Python code to auto generate .py support")
+ print(" >>> from win32com.client import gencache")
+ print(" >>> gencache.EnsureModule('%s', %s, %s, %s)" % (tlbSpec.clsid, tlbSpec.lcid, tlbSpec.major, tlbSpec.minor))
+
+class SimpleProgress(genpy.GeneratorProgress):
+ """A simple progress class prints its output to stderr
+ """
+ def __init__(self, verboseLevel):
+ self.verboseLevel = verboseLevel
+ def Close(self):
+ pass
+ def Finished(self):
+ if self.verboseLevel>1:
+ sys.stderr.write("Generation complete..\n")
+ def SetDescription(self, desc, maxticks = None):
+ if self.verboseLevel:
+ sys.stderr.write(desc + "\n")
+ def Tick(self, desc = None):
+ pass
+
+ def VerboseProgress(self, desc, verboseLevel = 2):
+ if self.verboseLevel >= verboseLevel:
+ sys.stderr.write(desc + "\n")
+
+ def LogBeginGenerate(self, filename):
+ self.VerboseProgress("Generating to %s" % filename, 1)
+
+ def LogWarning(self, desc):
+ self.VerboseProgress("WARNING: " + desc, 1)
+
+class GUIProgress(SimpleProgress):
+ def __init__(self, verboseLevel):
+ # Import some modules we need to we can trap failure now.
+ import win32ui, pywin
+ SimpleProgress.__init__(self, verboseLevel)
+ self.dialog = None
+
+ def Close(self):
+ if self.dialog is not None:
+ self.dialog.Close()
+ self.dialog = None
+
+ def Starting(self, tlb_desc):
+ SimpleProgress.Starting(self, tlb_desc)
+ if self.dialog is None:
+ from pywin.dialogs import status
+ self.dialog=status.ThreadedStatusProgressDialog(tlb_desc)
+ else:
+ self.dialog.SetTitle(tlb_desc)
+
+ def SetDescription(self, desc, maxticks = None):
+ self.dialog.SetText(desc)
+ if maxticks:
+ self.dialog.SetMaxTicks(maxticks)
+
+ def Tick(self, desc = None):
+ self.dialog.Tick()
+ if desc is not None:
+ self.dialog.SetText(desc)
+
+def GetTypeLibsForSpec(arg):
+ """Given an argument on the command line (either a file name, library
+ description, or ProgID of an object) return a list of actual typelibs
+ to use. """
+ typelibs = []
+ try:
+ try:
+ tlb = pythoncom.LoadTypeLib(arg)
+ spec = selecttlb.TypelibSpec(None, 0,0,0)
+ spec.FromTypelib(tlb, arg)
+ typelibs.append((tlb, spec))
+ except pythoncom.com_error:
+ # See if it is a description
+ tlbs = selecttlb.FindTlbsWithDescription(arg)
+ if len(tlbs)==0:
+ # Maybe it is the name of a COM object?
+ try:
+ ob = Dispatch(arg)
+ # and if so, it must support typelib info
+ tlb, index = ob._oleobj_.GetTypeInfo().GetContainingTypeLib()
+ spec = selecttlb.TypelibSpec(None, 0,0,0)
+ spec.FromTypelib(tlb)
+ tlbs.append(spec)
+ except pythoncom.com_error:
+ pass
+ if len(tlbs)==0:
+ print("Could not locate a type library matching '%s'" % (arg))
+ for spec in tlbs:
+ # Version numbers not always reliable if enumerated from registry.
+ # (as some libs use hex, other's dont. Both examples from MS, of course.)
+ if spec.dll is None:
+ tlb = pythoncom.LoadRegTypeLib(spec.clsid, spec.major, spec.minor, spec.lcid)
+ else:
+ tlb = pythoncom.LoadTypeLib(spec.dll)
+
+ # We have a typelib, but it may not be exactly what we specified
+ # (due to automatic version matching of COM). So we query what we really have!
+ attr = tlb.GetLibAttr()
+ spec.major = attr[3]
+ spec.minor = attr[4]
+ spec.lcid = attr[1]
+ typelibs.append((tlb, spec))
+ return typelibs
+ except pythoncom.com_error:
+ t,v,tb=sys.exc_info()
+ sys.stderr.write ("Unable to load type library from '%s' - %s\n" % (arg, v))
+ tb = None # Storing tb in a local is a cycle!
+ sys.exit(1)
+
+def GenerateFromTypeLibSpec(typelibInfo, file = None, verboseLevel = None, progressInstance = None, bUnicodeToString=None, bForDemand = bForDemandDefault, bBuildHidden = 1):
+ assert bUnicodeToString is None, "this is deprecated and will go away"
+ if verboseLevel is None:
+ verboseLevel = 0 # By default, we use no gui and no verbose level!
+
+ if bForDemand and file is not None:
+ raise RuntimeError("You can only perform a demand-build when the output goes to the gen_py directory")
+ if isinstance(typelibInfo, tuple):
+ # Tuple
+ typelibCLSID, lcid, major, minor = typelibInfo
+ tlb = pythoncom.LoadRegTypeLib(typelibCLSID, major, minor, lcid)
+ spec = selecttlb.TypelibSpec(typelibCLSID, lcid, major, minor)
+ spec.FromTypelib(tlb, str(typelibCLSID))
+ typelibs = [(tlb, spec)]
+ elif isinstance(typelibInfo, selecttlb.TypelibSpec):
+ if typelibInfo.dll is None:
+ # Version numbers not always reliable if enumerated from registry.
+ tlb = pythoncom.LoadRegTypeLib(typelibInfo.clsid, typelibInfo.major, typelibInfo.minor, typelibInfo.lcid)
+ else:
+ tlb = pythoncom.LoadTypeLib(typelibInfo.dll)
+ typelibs = [(tlb, typelibInfo)]
+ elif hasattr(typelibInfo, "GetLibAttr"):
+ # A real typelib object!
+ # Could also use isinstance(typelibInfo, PyITypeLib) instead, but PyITypeLib is not directly exposed by pythoncom.
+ # pythoncom.TypeIIDs[pythoncom.IID_ITypeLib] seems to work
+ tla = typelibInfo.GetLibAttr()
+ guid = tla[0]
+ lcid = tla[1]
+ major = tla[3]
+ minor = tla[4]
+ spec = selecttlb.TypelibSpec(guid, lcid, major, minor)
+ typelibs = [(typelibInfo, spec)]
+ else:
+ typelibs = GetTypeLibsForSpec(typelibInfo)
+
+ if progressInstance is None:
+ progressInstance = SimpleProgress(verboseLevel)
+ progress = progressInstance
+
+ bToGenDir = (file is None)
+
+ for typelib, info in typelibs:
+ gen = genpy.Generator(typelib, info.dll, progress, bBuildHidden=bBuildHidden)
+
+ if file is None:
+ this_name = gencache.GetGeneratedFileName(info.clsid, info.lcid, info.major, info.minor)
+ full_name = os.path.join(gencache.GetGeneratePath(), this_name)
+ if bForDemand:
+ try: os.unlink(full_name + ".py")
+ except os.error: pass
+ try: os.unlink(full_name + ".pyc")
+ except os.error: pass
+ try: os.unlink(full_name + ".pyo")
+ except os.error: pass
+ if not os.path.isdir(full_name):
+ os.mkdir(full_name)
+ outputName = os.path.join(full_name, "__init__.py")
+ else:
+ outputName = full_name + ".py"
+ fileUse = gen.open_writer(outputName)
+ progress.LogBeginGenerate(outputName)
+ else:
+ fileUse = file
+
+ worked = False
+ try:
+ gen.generate(fileUse, bForDemand)
+ worked = True
+ finally:
+ if file is None:
+ gen.finish_writer(outputName, fileUse, worked)
+ if bToGenDir:
+ progress.SetDescription("Importing module")
+ gencache.AddModuleToCache(info.clsid, info.lcid, info.major, info.minor)
+
+ progress.Close()
+
+def GenerateChildFromTypeLibSpec(child, typelibInfo, verboseLevel = None, progressInstance = None, bUnicodeToString=None):
+ assert bUnicodeToString is None, "this is deprecated and will go away"
+ if verboseLevel is None:
+ verboseLevel = 0 # By default, we use no gui, and no verbose level for the children.
+ if type(typelibInfo)==type(()):
+ typelibCLSID, lcid, major, minor = typelibInfo
+ tlb = pythoncom.LoadRegTypeLib(typelibCLSID, major, minor, lcid)
+ else:
+ tlb = typelibInfo
+ tla = typelibInfo.GetLibAttr()
+ typelibCLSID = tla[0]
+ lcid = tla[1]
+ major = tla[3]
+ minor = tla[4]
+ spec = selecttlb.TypelibSpec(typelibCLSID, lcid, major, minor)
+ spec.FromTypelib(tlb, str(typelibCLSID))
+ typelibs = [(tlb, spec)]
+
+ if progressInstance is None:
+ progressInstance = SimpleProgress(verboseLevel)
+ progress = progressInstance
+
+ for typelib, info in typelibs:
+ dir_name = gencache.GetGeneratedFileName(info.clsid, info.lcid, info.major, info.minor)
+ dir_path_name = os.path.join(gencache.GetGeneratePath(), dir_name)
+ progress.LogBeginGenerate(dir_path_name)
+
+ gen = genpy.Generator(typelib, info.dll, progress)
+ gen.generate_child(child, dir_path_name)
+ progress.SetDescription("Importing module")
+ __import__("win32com.gen_py." + dir_name + "." + child)
+ progress.Close()
+
+def main():
+ import getopt
+ hiddenSpec = 1
+ outputName = None
+ verboseLevel = 1
+ doit = 1
+ bForDemand = bForDemandDefault
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], 'vo:huiqd')
+ for o,v in opts:
+ if o=='-h':
+ hiddenSpec = 0
+ elif o=='-o':
+ outputName = v
+ elif o=='-v':
+ verboseLevel = verboseLevel + 1
+ elif o=='-q':
+ verboseLevel = verboseLevel - 1
+ elif o=='-i':
+ if len(args)==0:
+ ShowInfo(None)
+ else:
+ for arg in args:
+ ShowInfo(arg)
+ doit = 0
+ elif o=='-d':
+ bForDemand = not bForDemand
+
+ except (getopt.error, error) as msg:
+ sys.stderr.write (str(msg) + "\n")
+ usage()
+
+ if bForDemand and outputName is not None:
+ sys.stderr.write("Can not use -d and -o together\n")
+ usage()
+
+ if not doit:
+ return 0
+ if len(args)==0:
+ rc = selecttlb.SelectTlb()
+ if rc is None:
+ sys.exit(1)
+ args = [ rc ]
+
+ if outputName is not None:
+ path = os.path.dirname(outputName)
+ if path is not '' and not os.path.exists(path):
+ os.makedirs(path)
+ if sys.version_info > (3,0):
+ f = open(outputName, "wt", encoding="mbcs")
+ else:
+ import codecs # not available in py3k.
+ f = codecs.open(outputName, "w", "mbcs")
+ else:
+ f = None
+
+ for arg in args:
+ GenerateFromTypeLibSpec(arg, f, verboseLevel = verboseLevel, bForDemand = bForDemand, bBuildHidden = hiddenSpec)
+
+ if f:
+ f.close()
+
+
+if __name__=='__main__':
+ rc = main()
+ if rc:
+ sys.exit(rc)
+ sys.exit(0)
diff --git a/Lib/site-packages/win32com/client/selecttlb.py b/Lib/site-packages/win32com/client/selecttlb.py
new file mode 100644
index 0000000..8a99494
--- /dev/null
+++ b/Lib/site-packages/win32com/client/selecttlb.py
@@ -0,0 +1,160 @@
+"""Utilities for selecting and enumerating the Type Libraries installed on the system
+"""
+
+import win32api, win32con, pythoncom
+
+class TypelibSpec:
+ def __init__(self, clsid, lcid, major, minor, flags=0):
+ self.clsid = str(clsid)
+ self.lcid = int(lcid)
+ # We avoid assuming 'major' or 'minor' are integers - when
+ # read from the registry there is some confusion about if
+ # they are base 10 or base 16 (they *should* be base 16, but
+ # how they are written is beyond our control.)
+ self.major = major
+ self.minor = minor
+ self.dll = None
+ self.desc = None
+ self.ver_desc = None
+ self.flags = flags
+ # For the SelectList
+ def __getitem__(self, item):
+ if item==0:
+ return self.ver_desc
+ raise IndexError("Cant index me!")
+
+ def __lt__(self, other): # rich-cmp/py3k-friendly version
+ me = (self.ver_desc or "").lower(), (self.desc or "").lower(), self.major, self.minor
+ them = (other.ver_desc or "").lower(), (other.desc or "").lower(), other.major, other.minor
+ return me < them
+
+ def __eq__(self, other): # rich-cmp/py3k-friendly version
+ return ((self.ver_desc or "").lower() == (other.ver_desc or "").lower() and
+ (self.desc or "").lower() == (other.desc or "").lower() and
+ self.major == other.major and
+ self.minor == other.minor)
+
+ def Resolve(self):
+ if self.dll is None:
+ return 0
+ tlb = pythoncom.LoadTypeLib(self.dll)
+ self.FromTypelib(tlb, None)
+ return 1
+
+ def FromTypelib(self, typelib, dllName = None):
+ la = typelib.GetLibAttr()
+ self.clsid = str(la[0])
+ self.lcid = la[1]
+ self.major = la[3]
+ self.minor = la[4]
+ if dllName:
+ self.dll = dllName
+
+def EnumKeys(root):
+ index = 0
+ ret = []
+ while 1:
+ try:
+ item = win32api.RegEnumKey(root, index)
+ except win32api.error:
+ break
+ try:
+ # Note this doesn't handle REG_EXPAND_SZ, but the implementation
+ # here doesn't need to - that is handled as the data is read.
+ val = win32api.RegQueryValue(root, item)
+ except win32api.error:
+ val = "" # code using this assumes a string.
+
+ ret.append((item, val))
+ index = index + 1
+ return ret
+
+FLAG_RESTRICTED=1
+FLAG_CONTROL=2
+FLAG_HIDDEN=4
+
+def EnumTlbs(excludeFlags = 0):
+ """Return a list of TypelibSpec objects, one for each registered library.
+ """
+ key = win32api.RegOpenKey(win32con.HKEY_CLASSES_ROOT, "Typelib")
+ iids = EnumKeys(key)
+ results = []
+ for iid, crap in iids:
+ try:
+ key2 = win32api.RegOpenKey(key, str(iid))
+ except win32api.error:
+ # A few good reasons for this, including "access denied".
+ continue
+ for version, tlbdesc in EnumKeys(key2):
+ major_minor = version.split('.', 1)
+ if len(major_minor) < 2:
+ major_minor.append('0')
+ # For some reason, this code used to assume the values were hex.
+ # This seems to not be true - particularly for CDO 1.21
+ # *sigh* - it appears there are no rules here at all, so when we need
+ # to know the info, we must load the tlb by filename and request it.
+ # The Resolve() method on the TypelibSpec does this.
+ # For this reason, keep the version numbers as strings - that
+ # way we can't be wrong! Let code that really needs an int to work
+ # out what to do. FWIW, http://support.microsoft.com/kb/816970 is
+ # pretty clear that they *should* be hex.
+ major = major_minor[0]
+ minor = major_minor[1]
+ key3 = win32api.RegOpenKey(key2, str(version))
+ try:
+ # The "FLAGS" are at this point
+ flags = int(win32api.RegQueryValue(key3, "FLAGS"))
+ except (win32api.error, ValueError):
+ flags = 0
+ if flags & excludeFlags==0:
+ for lcid, crap in EnumKeys(key3):
+ try:
+ lcid = int(lcid)
+ except ValueError: # not an LCID entry
+ continue
+ # Only care about "{lcid}\win32" key - jump straight there.
+ try:
+ key4 = win32api.RegOpenKey(key3, "%s\\win32" % (lcid,))
+ except win32api.error:
+ continue
+ try:
+ dll, typ = win32api.RegQueryValueEx(key4, None)
+ if typ==win32con.REG_EXPAND_SZ:
+ dll = win32api.ExpandEnvironmentStrings(dll)
+ except win32api.error:
+ dll = None
+ spec = TypelibSpec(iid, lcid, major, minor, flags)
+ spec.dll = dll
+ spec.desc = tlbdesc
+ spec.ver_desc = tlbdesc + " (" + version + ")"
+ results.append(spec)
+ return results
+
+def FindTlbsWithDescription(desc):
+ """Find all installed type libraries with the specified description
+ """
+ ret = []
+ items = EnumTlbs()
+ for item in items:
+ if item.desc==desc:
+ ret.append(item)
+ return ret
+
+def SelectTlb(title="Select Library", excludeFlags = 0):
+ """Display a list of all the type libraries, and select one. Returns None if cancelled
+ """
+ import pywin.dialogs.list
+ items = EnumTlbs(excludeFlags)
+ # fixup versions - we assume hex (see __init__ above)
+ for i in items:
+ i.major = int(i.major, 16)
+ i.minor = int(i.minor, 16)
+ items.sort()
+ rc = pywin.dialogs.list.SelectFromLists(title, items, ["Type Library"])
+ if rc is None:
+ return None
+ return items[rc]
+
+# Test code.
+if __name__=='__main__':
+ print(SelectTlb().__dict__)
diff --git a/Lib/site-packages/win32com/client/tlbrowse.py b/Lib/site-packages/win32com/client/tlbrowse.py
new file mode 100644
index 0000000..5ad8d6c
--- /dev/null
+++ b/Lib/site-packages/win32com/client/tlbrowse.py
@@ -0,0 +1,243 @@
+import win32ui
+import win32con
+import win32api
+import commctrl
+import pythoncom
+from pywin.mfc import dialog
+
+class TLBrowserException(Exception):
+ "TypeLib browser internal error"
+error = TLBrowserException
+
+FRAMEDLG_STD = win32con.WS_CAPTION | win32con.WS_SYSMENU
+SS_STD = win32con.WS_CHILD | win32con.WS_VISIBLE
+BS_STD = SS_STD | win32con.WS_TABSTOP
+ES_STD = BS_STD | win32con.WS_BORDER
+LBS_STD = ES_STD | win32con.LBS_NOTIFY | win32con.LBS_NOINTEGRALHEIGHT | win32con.WS_VSCROLL
+CBS_STD = ES_STD | win32con.CBS_NOINTEGRALHEIGHT | win32con.WS_VSCROLL
+
+typekindmap = {
+ pythoncom.TKIND_ENUM : 'Enumeration',
+ pythoncom.TKIND_RECORD : 'Record',
+ pythoncom.TKIND_MODULE : 'Module',
+ pythoncom.TKIND_INTERFACE : 'Interface',
+ pythoncom.TKIND_DISPATCH : 'Dispatch',
+ pythoncom.TKIND_COCLASS : 'CoClass',
+ pythoncom.TKIND_ALIAS : 'Alias',
+ pythoncom.TKIND_UNION : 'Union'
+}
+
+TypeBrowseDialog_Parent=dialog.Dialog
+class TypeBrowseDialog(TypeBrowseDialog_Parent):
+ "Browse a type library"
+
+ IDC_TYPELIST = 1000
+ IDC_MEMBERLIST = 1001
+ IDC_PARAMLIST = 1002
+ IDC_LISTVIEW = 1003
+
+ def __init__(self, typefile = None):
+ TypeBrowseDialog_Parent.__init__(self, self.GetTemplate())
+ try:
+ if typefile:
+ self.tlb = pythoncom.LoadTypeLib(typefile)
+ else:
+ self.tlb = None
+ except pythoncom.ole_error:
+ self.MessageBox("The file does not contain type information")
+ self.tlb = None
+ self.HookCommand(self.CmdTypeListbox, self.IDC_TYPELIST)
+ self.HookCommand(self.CmdMemberListbox, self.IDC_MEMBERLIST)
+
+ def OnAttachedObjectDeath(self):
+ self.tlb = None
+ self.typeinfo = None
+ self.attr = None
+ return TypeBrowseDialog_Parent.OnAttachedObjectDeath(self)
+
+ def _SetupMenu(self):
+ menu = win32ui.CreateMenu()
+ flags=win32con.MF_STRING|win32con.MF_ENABLED
+ menu.AppendMenu(flags, win32ui.ID_FILE_OPEN, "&Open...")
+ menu.AppendMenu(flags, win32con.IDCANCEL, "&Close")
+ mainMenu = win32ui.CreateMenu()
+ mainMenu.AppendMenu(flags|win32con.MF_POPUP, menu.GetHandle(), "&File")
+ self.SetMenu(mainMenu)
+ self.HookCommand(self.OnFileOpen,win32ui.ID_FILE_OPEN)
+
+ def OnFileOpen(self, id, code):
+ openFlags = win32con.OFN_OVERWRITEPROMPT | win32con.OFN_FILEMUSTEXIST
+ fspec = "Type Libraries (*.tlb, *.olb)|*.tlb;*.olb|OCX Files (*.ocx)|*.ocx|DLL's (*.dll)|*.dll|All Files (*.*)|*.*||"
+ dlg = win32ui.CreateFileDialog(1, None, None, openFlags, fspec)
+ if dlg.DoModal() == win32con.IDOK:
+ try:
+ self.tlb = pythoncom.LoadTypeLib(dlg.GetPathName())
+ except pythoncom.ole_error:
+ self.MessageBox("The file does not contain type information")
+ self.tlb = None
+ self._SetupTLB()
+
+ def OnInitDialog(self):
+ self._SetupMenu()
+ self.typelb = self.GetDlgItem(self.IDC_TYPELIST)
+ self.memberlb = self.GetDlgItem(self.IDC_MEMBERLIST)
+ self.paramlb = self.GetDlgItem(self.IDC_PARAMLIST)
+ self.listview = self.GetDlgItem(self.IDC_LISTVIEW)
+
+ # Setup the listview columns
+ itemDetails = (commctrl.LVCFMT_LEFT, 100, "Item", 0)
+ self.listview.InsertColumn(0, itemDetails)
+ itemDetails = (commctrl.LVCFMT_LEFT, 1024, "Details", 0)
+ self.listview.InsertColumn(1, itemDetails)
+
+ if self.tlb is None:
+ self.OnFileOpen(None,None)
+ else:
+ self._SetupTLB()
+ return TypeBrowseDialog_Parent.OnInitDialog(self)
+
+ def _SetupTLB(self):
+ self.typelb.ResetContent()
+ self.memberlb.ResetContent()
+ self.paramlb.ResetContent()
+ self.typeinfo = None
+ self.attr = None
+ if self.tlb is None: return
+ n = self.tlb.GetTypeInfoCount()
+ for i in range(n):
+ self.typelb.AddString(self.tlb.GetDocumentation(i)[0])
+
+ def _SetListviewTextItems(self, items):
+ self.listview.DeleteAllItems()
+ index = -1
+ for item in items:
+ index = self.listview.InsertItem(index+1,item[0])
+ data = item[1]
+ if data is None: data = ""
+ self.listview.SetItemText(index, 1, data)
+
+ def SetupAllInfoTypes(self):
+ infos = self._GetMainInfoTypes() + self._GetMethodInfoTypes()
+ self._SetListviewTextItems(infos)
+
+ def _GetMainInfoTypes(self):
+ pos = self.typelb.GetCurSel()
+ if pos<0: return []
+ docinfo = self.tlb.GetDocumentation(pos)
+ infos = [('GUID', str(self.attr[0]))]
+ infos.append(('Help File', docinfo[3]))
+ infos.append(('Help Context', str(docinfo[2])))
+ try:
+ infos.append(('Type Kind', typekindmap[self.tlb.GetTypeInfoType(pos)]))
+ except:
+ pass
+
+ info = self.tlb.GetTypeInfo(pos)
+ attr = info.GetTypeAttr()
+ infos.append(('Attributes', str(attr)))
+
+ for j in range(attr[8]):
+ flags = info.GetImplTypeFlags(j)
+ refInfo = info.GetRefTypeInfo(info.GetRefTypeOfImplType(j))
+ doc = refInfo.GetDocumentation(-1)
+ attr = refInfo.GetTypeAttr()
+ typeKind = attr[5]
+ typeFlags = attr[11]
+
+ desc = doc[0]
+ desc = desc + ", Flags=0x%x, typeKind=0x%x, typeFlags=0x%x" % (flags, typeKind, typeFlags)
+ if flags & pythoncom.IMPLTYPEFLAG_FSOURCE:
+ desc = desc + "(Source)"
+ infos.append( ('Implements', desc))
+
+ return infos
+
+ def _GetMethodInfoTypes(self):
+ pos = self.memberlb.GetCurSel()
+ if pos<0: return []
+
+ realPos, isMethod = self._GetRealMemberPos(pos)
+ ret = []
+ if isMethod:
+ funcDesc = self.typeinfo.GetFuncDesc(realPos)
+ id = funcDesc[0]
+ ret.append(("Func Desc", str(funcDesc)))
+ else:
+ id = self.typeinfo.GetVarDesc(realPos)[0]
+
+ docinfo = self.typeinfo.GetDocumentation(id)
+ ret.append(('Help String', docinfo[1]))
+ ret.append(('Help Context', str(docinfo[2])))
+ return ret
+
+ def CmdTypeListbox(self, id, code):
+ if code == win32con.LBN_SELCHANGE:
+ pos = self.typelb.GetCurSel()
+ if pos >= 0:
+ self.memberlb.ResetContent()
+ self.typeinfo = self.tlb.GetTypeInfo(pos)
+ self.attr = self.typeinfo.GetTypeAttr()
+ for i in range(self.attr[7]):
+ id = self.typeinfo.GetVarDesc(i)[0]
+ self.memberlb.AddString(self.typeinfo.GetNames(id)[0])
+ for i in range(self.attr[6]):
+ id = self.typeinfo.GetFuncDesc(i)[0]
+ self.memberlb.AddString(self.typeinfo.GetNames(id)[0])
+ self.SetupAllInfoTypes()
+ return 1
+
+ def _GetRealMemberPos(self, pos):
+ pos = self.memberlb.GetCurSel()
+ if pos >= self.attr[7]:
+ return pos - self.attr[7], 1
+ elif pos >= 0:
+ return pos, 0
+ else:
+ raise error("The position is not valid")
+
+ def CmdMemberListbox(self, id, code):
+ if code == win32con.LBN_SELCHANGE:
+ self.paramlb.ResetContent()
+ pos = self.memberlb.GetCurSel()
+ realPos, isMethod = self._GetRealMemberPos(pos)
+ if isMethod:
+ id = self.typeinfo.GetFuncDesc(realPos)[0]
+ names = self.typeinfo.GetNames(id)
+ for i in range(len(names)):
+ if i > 0:
+ self.paramlb.AddString(names[i])
+ self.SetupAllInfoTypes()
+ return 1
+
+ def GetTemplate(self):
+ "Return the template used to create this dialog"
+
+ w = 272 # Dialog width
+ h = 192 # Dialog height
+ style = FRAMEDLG_STD | win32con.WS_VISIBLE | win32con.DS_SETFONT | win32con.WS_MINIMIZEBOX
+ template = [['Type Library Browser', (0, 0, w, h), style, None, (8, 'Helv')], ]
+ template.append([130, "&Type", -1, (10, 10, 62, 9), SS_STD | win32con.SS_LEFT])
+ template.append([131, None, self.IDC_TYPELIST, (10, 20, 80, 80), LBS_STD])
+ template.append([130, "&Members", -1, (100, 10, 62, 9), SS_STD | win32con.SS_LEFT])
+ template.append([131, None, self.IDC_MEMBERLIST, (100, 20, 80, 80), LBS_STD])
+ template.append([130, "&Parameters", -1, (190, 10, 62, 9), SS_STD | win32con.SS_LEFT])
+ template.append([131, None, self.IDC_PARAMLIST, (190, 20, 75, 80), LBS_STD])
+
+ lvStyle = SS_STD | commctrl.LVS_REPORT | commctrl.LVS_AUTOARRANGE | commctrl.LVS_ALIGNLEFT | win32con.WS_BORDER | win32con.WS_TABSTOP
+ template.append(["SysListView32", "", self.IDC_LISTVIEW, (10, 110, 255, 65), lvStyle])
+
+ return template
+
+if __name__=='__main__':
+ import sys
+ fname = None
+ try:
+ fname = sys.argv[1]
+ except:
+ pass
+ dlg = TypeBrowseDialog(fname)
+ try:
+ win32api.GetConsoleTitle()
+ dlg.DoModal()
+ except:
+ dlg.CreateWindow(win32ui.GetMainFrame())
diff --git a/Lib/site-packages/win32com/client/util.py b/Lib/site-packages/win32com/client/util.py
new file mode 100644
index 0000000..bdbfb8c
--- /dev/null
+++ b/Lib/site-packages/win32com/client/util.py
@@ -0,0 +1,84 @@
+"""General client side utilities.
+
+This module contains utility functions, used primarily by advanced COM
+programmers, or other COM modules.
+"""
+import pythoncom
+from win32com.client import Dispatch, _get_good_object_
+
+PyIDispatchType = pythoncom.TypeIIDs[pythoncom.IID_IDispatch]
+
+def WrapEnum(ob, resultCLSID = None):
+ """Wrap an object in a VARIANT enumerator.
+
+ All VT_DISPATCHs returned by the enumerator are converted to wrapper objects
+ (which may be either a class instance, or a dynamic.Dispatch type object).
+
+ """
+ if type(ob) != pythoncom.TypeIIDs[pythoncom.IID_IEnumVARIANT]:
+ ob = ob.QueryInterface(pythoncom.IID_IEnumVARIANT)
+ return EnumVARIANT(ob, resultCLSID)
+
+class Enumerator:
+ """A class that provides indexed access into an Enumerator
+
+ By wrapping a PyIEnum* object in this class, you can perform
+ natural looping and indexing into the Enumerator.
+
+ Looping is very efficient, but it should be noted that although random
+ access is supported, the underlying object is still an enumerator, so
+ this will force many reset-and-seek operations to find the requested index.
+
+ """
+ def __init__(self, enum):
+ self._oleobj_ = enum # a PyIEnumVARIANT
+ self.index = -1
+ def __getitem__(self, index):
+ return self.__GetIndex(index)
+ def __call__(self, index):
+ return self.__GetIndex(index)
+
+ def __GetIndex(self, index):
+ if type(index)!=type(0): raise TypeError("Only integer indexes are supported for enumerators")
+ # NOTE
+ # In this context, self.index is users purely as a flag to say
+ # "am I still in sequence". The user may call Next() or Reset() if they
+ # so choose, in which case self.index will not be correct (although we
+ # still want to stay in sequence)
+ if index != self.index + 1:
+ # Index requested out of sequence.
+ self._oleobj_.Reset()
+ if index: self._oleobj_.Skip(index) # if asked for item 1, must skip 1, Python always zero based.
+ self.index = index
+ result = self._oleobj_.Next(1)
+ if len(result):
+ return self._make_retval_(result[0])
+ raise IndexError("list index out of range")
+ def Next(self, count=1):
+ ret = self._oleobj_.Next(count)
+ realRets = []
+ for r in ret:
+ realRets.append(self._make_retval_(r))
+ return tuple(realRets) # Convert back to tuple.
+ def Reset(self):
+ return self._oleobj_.Reset()
+ def Clone(self):
+ return self.__class__( self._oleobj_.Clone(), self.resultCLSID)
+ def _make_retval_(self, result):
+ return result
+
+class EnumVARIANT(Enumerator):
+ def __init__(self, enum, resultCLSID = None):
+ self.resultCLSID = resultCLSID
+ Enumerator.__init__(self, enum)
+ def _make_retval_(self, result):
+ return _get_good_object_(result, resultCLSID = self.resultCLSID)
+
+class Iterator:
+ def __init__(self, enum, resultCLSID = None):
+ self.resultCLSID = resultCLSID
+ self._iter_ = iter(enum.QueryInterface(pythoncom.IID_IEnumVARIANT))
+ def __iter__(self):
+ return self
+ def __next__(self):
+ return _get_good_object_(next(self._iter_), resultCLSID = self.resultCLSID)
diff --git a/Lib/site-packages/win32com/demos/__init__.py b/Lib/site-packages/win32com/demos/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/Lib/site-packages/win32com/demos/connect.py b/Lib/site-packages/win32com/demos/connect.py
new file mode 100644
index 0000000..992965e
--- /dev/null
+++ b/Lib/site-packages/win32com/demos/connect.py
@@ -0,0 +1,83 @@
+# Implements _both_ a connectable client, and a connectable server.
+#
+# Note that we cheat just a little - the Server in this demo is not created
+# via Normal COM - this means we can avoid registering the server.
+# However, the server _is_ accessed as a COM object - just the creation
+# is cheated on - so this is still working as a fully-fledged server.
+
+import pythoncom
+import win32com.server.util
+import win32com.server.connect
+from win32com.server.exception import Exception
+from pywin32_testutil import str2bytes
+
+# This is the IID of the Events interface both Client and Server support.
+IID_IConnectDemoEvents = pythoncom.MakeIID("{A4988850-49C3-11d0-AE5D-52342E000000}")
+
+# The server which implements
+# Create a connectable class, that has a single public method
+# 'DoIt', which echos to a single sink 'DoneIt'
+
+class ConnectableServer(win32com.server.connect.ConnectableServer):
+ _public_methods_ = ["DoIt"] + win32com.server.connect.ConnectableServer._public_methods_
+ _connect_interfaces_ = [IID_IConnectDemoEvents]
+ # The single public method that the client can call on us
+ # (ie, as a normal COM server, this exposes just this single method.
+ def DoIt(self,arg):
+ # Simply broadcast a notification.
+ self._BroadcastNotify(self.NotifyDoneIt, (arg,))
+
+ def NotifyDoneIt(self, interface, arg):
+ interface.Invoke(1000, 0, pythoncom.DISPATCH_METHOD, 1, arg)
+
+# Here is the client side of the connection world.
+# Define a COM object which implements the methods defined by the
+# IConnectDemoEvents interface.
+class ConnectableClient:
+ # This is another cheat - I _know_ the server defines the "DoneIt" event
+ # as DISPID==1000 - I also know from the implementation details of COM
+ # that the first method in _public_methods_ gets 1000.
+ # Normally some explicit DISPID->Method mapping is required.
+ _public_methods_ = ["OnDoneIt"]
+ def __init__(self):
+ self.last_event_arg = None
+ # A client must implement QI, and respond to a query for the Event interface.
+ # In addition, it must provide a COM object (which server.util.wrap) does.
+ def _query_interface_(self, iid):
+ import win32com.server.util
+ # Note that this seems like a necessary hack. I am responding to IID_IConnectDemoEvents
+ # but only creating an IDispatch gateway object.
+ if iid==IID_IConnectDemoEvents: return win32com.server.util.wrap(self)
+ # And here is our event method which gets called.
+ def OnDoneIt(self, arg):
+ self.last_event_arg = arg
+
+def CheckEvent(server, client, val, verbose):
+ client.last_event_arg = None
+ server.DoIt(val)
+ if client.last_event_arg != val:
+ raise RuntimeError("Sent %r, but got back %r" % (val, client.last_event_arg))
+ if verbose:
+ print("Sent and received %r" % val)
+
+# A simple test script for all this.
+# In the real world, it is likely that the code controlling the server
+# will be in the same class as that getting the notifications.
+def test(verbose=0):
+ import win32com.client.dynamic, win32com.client.connect
+ import win32com.server.policy
+ server = win32com.client.dynamic.Dispatch(win32com.server.util.wrap(ConnectableServer()))
+ connection = win32com.client.connect.SimpleConnection()
+ client = ConnectableClient()
+ connection.Connect(server, client, IID_IConnectDemoEvents)
+ CheckEvent(server, client, "Hello", verbose)
+ CheckEvent(server, client, str2bytes("Here is a null>\x00<"), verbose)
+ CheckEvent(server, client, "Here is a null>\x00<", verbose)
+ val = "test-\xe0\xf2" # 2 extended characters.
+ CheckEvent(server, client, val, verbose)
+ if verbose:
+ print("Everything seemed to work!")
+ # Aggressive memory leak checking (ie, do nothing!) :-) All should cleanup OK???
+
+if __name__=='__main__':
+ test(1)
diff --git a/Lib/site-packages/win32com/demos/dump_clipboard.py b/Lib/site-packages/win32com/demos/dump_clipboard.py
new file mode 100644
index 0000000..d41b300
--- /dev/null
+++ b/Lib/site-packages/win32com/demos/dump_clipboard.py
@@ -0,0 +1,68 @@
+import pythoncom
+import win32con
+
+formats = """CF_TEXT CF_BITMAP CF_METAFILEPICT CF_SYLK CF_DIF CF_TIFF
+ CF_OEMTEXT CF_DIB CF_PALETTE CF_PENDATA CF_RIFF CF_WAVE
+ CF_UNICODETEXT CF_ENHMETAFILE CF_HDROP CF_LOCALE CF_MAX
+ CF_OWNERDISPLAY CF_DSPTEXT CF_DSPBITMAP CF_DSPMETAFILEPICT
+ CF_DSPENHMETAFILE""".split()
+format_name_map = {}
+for f in formats:
+ val = getattr(win32con, f)
+ format_name_map[val]=f
+
+tymeds = [attr for attr in pythoncom.__dict__.keys() if attr.startswith("TYMED_")]
+
+def DumpClipboard():
+ do = pythoncom.OleGetClipboard()
+ print("Dumping all clipboard formats...")
+ for fe in do.EnumFormatEtc():
+ fmt, td, aspect, index, tymed = fe
+ tymeds_this = [getattr(pythoncom, t) for t in tymeds if tymed & getattr(pythoncom, t)]
+ print("Clipboard format", format_name_map.get(fmt,str(fmt)))
+ for t_this in tymeds_this:
+ # As we are enumerating there should be no need to call
+ # QueryGetData, but we do anyway!
+ fetc_query = fmt, td, aspect, index, t_this
+ try:
+ do.QueryGetData(fetc_query)
+ except pythoncom.com_error:
+ print("Eeek - QGD indicated failure for tymed", t_this)
+ # now actually get it.
+ try:
+ medium = do.GetData(fetc_query)
+ except pythoncom.com_error as exc:
+ print("Failed to get the clipboard data:", exc)
+ continue
+ if medium.tymed==pythoncom.TYMED_GDI:
+ data = "GDI handle %d" % medium.data
+ elif medium.tymed==pythoncom.TYMED_MFPICT:
+ data = "METAFILE handle %d" % medium.data
+ elif medium.tymed==pythoncom.TYMED_ENHMF:
+ data = "ENHMETAFILE handle %d" % medium.data
+ elif medium.tymed==pythoncom.TYMED_HGLOBAL:
+ data = "%d bytes via HGLOBAL" % len(medium.data)
+ elif medium.tymed==pythoncom.TYMED_FILE:
+ data = "filename '%s'" % data
+ elif medium.tymed==pythoncom.TYMED_ISTREAM:
+ stream = medium.data
+ stream.Seek(0,0)
+ bytes = 0
+ while 1:
+ chunk = stream.Read(4096)
+ if not chunk:
+ break
+ bytes += len(chunk)
+ data = "%d bytes via IStream" % bytes
+ elif medium.tymed==pythoncom.TYMED_ISTORAGE:
+ data = "a IStorage"
+ else:
+ data = "*** unknown tymed!"
+ print(" -> got", data)
+ do = None
+
+if __name__=='__main__':
+ DumpClipboard()
+ if pythoncom._GetInterfaceCount()+pythoncom._GetGatewayCount():
+ print("XXX - Leaving with %d/%d COM objects alive" % \
+ (pythoncom._GetInterfaceCount(), pythoncom._GetGatewayCount()))
diff --git a/Lib/site-packages/win32com/demos/eventsApartmentThreaded.py b/Lib/site-packages/win32com/demos/eventsApartmentThreaded.py
new file mode 100644
index 0000000..cf85322
--- /dev/null
+++ b/Lib/site-packages/win32com/demos/eventsApartmentThreaded.py
@@ -0,0 +1,94 @@
+# A sample originally provided by Richard Bell, and modified by Mark Hammond.
+
+# This sample demonstrates how to use COM events in an aparment-threaded
+# world. In this world, COM itself ensures that all calls to and events
+# from an object happen on the same thread that created the object, even
+# if they originated from different threads. For this cross-thread
+# marshalling to work, this main thread *must* run a "message-loop" (ie,
+# a loop fetching and dispatching Windows messages). Without such message
+# processing, dead-locks can occur.
+
+# See also eventsFreeThreaded.py for how to do this in a free-threaded
+# world where these marshalling considerations do not exist.
+
+# NOTE: This example uses Internet Explorer, but it should not be considerd
+# a "best-practices" for writing against IE events, but for working with
+# events in general. For example:
+# * The first OnDocumentComplete event is not a reliable indicator that the
+# URL has completed loading
+# * As we are demonstrating the most efficient way of handling events, when
+# running this sample you will see an IE Windows briefly appear, but
+# vanish without ever being repainted.
+
+import sys
+import os
+import win32com.client
+import win32api
+import win32event
+# sys.coinit_flags not set, so pythoncom initializes apartment-threaded.
+import pythoncom
+import time
+
+class ExplorerEvents:
+ def __init__(self):
+ self.event = win32event.CreateEvent(None, 0, 0, None)
+ def OnDocumentComplete(self,
+ pDisp=pythoncom.Empty,
+ URL=pythoncom.Empty):
+ thread = win32api.GetCurrentThreadId()
+ print("OnDocumentComplete event processed on thread %d"%thread)
+ # Set the event our main thread is waiting on.
+ win32event.SetEvent(self.event)
+ def OnQuit(self):
+ thread = win32api.GetCurrentThreadId()
+ print("OnQuit event processed on thread %d"%thread)
+ win32event.SetEvent(self.event)
+
+def WaitWhileProcessingMessages(event, timeout = 2):
+ start = time.clock()
+ while True:
+ # Wake 4 times a second - we can't just specify the
+ # full timeout here, as then it would reset for every
+ # message we process.
+ rc = win32event.MsgWaitForMultipleObjects( (event,), 0,
+ 250,
+ win32event.QS_ALLEVENTS)
+ if rc == win32event.WAIT_OBJECT_0:
+ # event signalled - stop now!
+ return True
+ if (time.clock() - start) > timeout:
+ # Timeout expired.
+ return False
+ # must be a message.
+ pythoncom.PumpWaitingMessages()
+
+def TestExplorerEvents():
+ iexplore = win32com.client.DispatchWithEvents(
+ "InternetExplorer.Application", ExplorerEvents)
+
+ thread = win32api.GetCurrentThreadId()
+ print('TestExplorerEvents created IE object on thread %d'%thread)
+
+ iexplore.Visible = 1
+ try:
+ iexplore.Navigate(win32api.GetFullPathName('..\\readme.htm'))
+ except pythoncom.com_error as details:
+ print("Warning - could not open the test HTML file", details)
+
+ # Wait for the event to be signalled while pumping messages.
+ if not WaitWhileProcessingMessages(iexplore.event):
+ print("Document load event FAILED to fire!!!")
+
+ iexplore.Quit()
+ #
+ # Give IE a chance to shutdown, else it can get upset on fast machines.
+ # Note, Quit generates events. Although this test does NOT catch them
+ # it is NECESSARY to pump messages here instead of a sleep so that the Quit
+ # happens properly!
+ if not WaitWhileProcessingMessages(iexplore.event):
+ print("OnQuit event FAILED to fire!!!")
+
+ iexplore = None
+
+if __name__=='__main__':
+ TestExplorerEvents()
diff --git a/Lib/site-packages/win32com/demos/eventsFreeThreaded.py b/Lib/site-packages/win32com/demos/eventsFreeThreaded.py
new file mode 100644
index 0000000..0c05a3c
--- /dev/null
+++ b/Lib/site-packages/win32com/demos/eventsFreeThreaded.py
@@ -0,0 +1,88 @@
+# A sample originally provided by Richard Bell, and modified by Mark Hammond.
+
+# This sample demonstrates how to use COM events in a free-threaded world.
+# In this world, there is no need to marshall calls across threads, so
+# no message loops are needed at all. This means regular cross-thread
+# sychronization can be used. In this sample we just wait on win32 event
+# objects.
+
+# See also ieEventsApartmentThreaded.py for how to do this in an
+# aparment-threaded world, where thread-marshalling complicates things.
+
+# NOTE: This example uses Internet Explorer, but it should not be considerd
+# a "best-practices" for writing against IE events, but for working with
+# events in general. For example:
+# * The first OnDocumentComplete event is not a reliable indicator that the
+# URL has completed loading
+# * As we are demonstrating the most efficient way of handling events, when
+# running this sample you will see an IE Windows briefly appear, but
+# vanish without ever being repainted.
+
+import sys
+sys.coinit_flags=0 # specify free threading
+
+import os
+import win32api
+import win32event
+import win32com.client
+import pythoncom
+import time
+
+# The print statements indicate that COM has actually started another thread
+# and will deliver the events to that thread (ie, the events do not actually
+# fire on our main thread.
+class ExplorerEvents:
+ def __init__(self):
+ # We reuse this event for all events.
+ self.event = win32event.CreateEvent(None, 0, 0, None)
+ def OnDocumentComplete(self,
+ pDisp=pythoncom.Empty,
+ URL=pythoncom.Empty):
+ #
+ # Caution: Since the main thread and events thread(s) are different
+ # it may be necessary to serialize access to shared data. Because
+ # this is a simple test case, that is not required here. Your
+ # situation may be different. Caveat programmer.
+ #
+ thread = win32api.GetCurrentThreadId()
+ print("OnDocumentComplete event processed on thread %d"%thread)
+ # Set the event our main thread is waiting on.
+ win32event.SetEvent(self.event)
+ def OnQuit(self):
+ thread = win32api.GetCurrentThreadId()
+ print("OnQuit event processed on thread %d"%thread)
+ win32event.SetEvent(self.event)
+
+def TestExplorerEvents():
+ iexplore = win32com.client.DispatchWithEvents(
+ "InternetExplorer.Application", ExplorerEvents)
+
+ thread = win32api.GetCurrentThreadId()
+ print('TestExplorerEvents created IE object on thread %d'%thread)
+
+ iexplore.Visible = 1
+ try:
+ iexplore.Navigate(win32api.GetFullPathName('..\\readme.htm'))
+ except pythoncom.com_error as details:
+ print("Warning - could not open the test HTML file", details)
+
+ # In this free-threaded example, we can simply wait until an event has
+ # been set - we will give it 2 seconds before giving up.
+ rc = win32event.WaitForSingleObject(iexplore.event, 2000)
+ if rc != win32event.WAIT_OBJECT_0:
+ print("Document load event FAILED to fire!!!")
+
+ iexplore.Quit()
+ # Now we can do the same thing to wait for exit!
+ # Although Quit generates events, in this free-threaded world we
+ # do *not* need to run any message pumps.
+
+ rc = win32event.WaitForSingleObject(iexplore.event, 2000)
+ if rc != win32event.WAIT_OBJECT_0:
+ print("OnQuit event FAILED to fire!!!")
+
+ iexplore = None
+ print("Finished the IE event sample!")
+
+if __name__=='__main__':
+ TestExplorerEvents()
diff --git a/Lib/site-packages/win32com/demos/excelAddin.py b/Lib/site-packages/win32com/demos/excelAddin.py
new file mode 100644
index 0000000..bb93217
--- /dev/null
+++ b/Lib/site-packages/win32com/demos/excelAddin.py
@@ -0,0 +1,139 @@
+# A demo plugin for Microsoft Excel
+#
+# This addin simply adds a new button to the main Excel toolbar,
+# and displays a message box when clicked. Thus, it demonstrates
+# how to plug in to Excel itself, and hook Excel events.
+#
+#
+# To register the addin, simply execute:
+# excelAddin.py
+# This will install the COM server, and write the necessary
+# AddIn key to Excel
+#
+# To unregister completely:
+# excelAddin.py --unregister
+#
+# To debug, execute:
+# excelAddin.py --debug
+#
+# Then open Pythonwin, and select "Tools->Trace Collector Debugging Tool"
+# Restart excel, and you should see some output generated.
+#
+# NOTE: If the AddIn fails with an error, Excel will re-register
+# the addin to not automatically load next time Excel starts. To
+# correct this, simply re-register the addin (see above)
+#
+# Author Eric Koome
+# Copyright (c) 2003 Wavecom Inc. All rights reserved
+#
+# Redistribution and use in source and binary forms, with or without
+#modification, are permitted provided that the following conditions
+#are met:
+#
+#1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL ERIC KOOME OR
+# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+from win32com import universal
+from win32com.server.exception import COMException
+from win32com.client import gencache, DispatchWithEvents
+import winerror
+import pythoncom
+from win32com.client import constants, Dispatch
+import sys
+
+# Support for COM objects we use.
+gencache.EnsureModule('{00020813-0000-0000-C000-000000000046}', 0, 1, 3, bForDemand=True) # Excel 9
+gencache.EnsureModule('{2DF8D04C-5BFA-101B-BDE5-00AA0044DE52}', 0, 2, 1, bForDemand=True) # Office 9
+
+# The TLB defiining the interfaces we implement
+universal.RegisterInterfaces('{AC0714F2-3D04-11D1-AE7D-00A0C90F26F4}', 0, 1, 0, ["_IDTExtensibility2"])
+class ButtonEvent:
+ def OnClick(self, button, cancel):
+ import win32ui # Possible, but not necessary, to use a Pythonwin GUI
+ import win32con
+ win32ui.MessageBox("Hello from Python", "Python Test",win32con.MB_OKCANCEL)
+ return cancel
+
+class ExcelAddin:
+ _com_interfaces_ = ['_IDTExtensibility2']
+ _public_methods_ = []
+ _reg_clsctx_ = pythoncom.CLSCTX_INPROC_SERVER
+ _reg_clsid_ = "{C5482ECA-F559-45A0-B078-B2036E6F011A}"
+ _reg_progid_ = "Python.Test.ExcelAddin"
+ _reg_policy_spec_ = "win32com.server.policy.EventHandlerPolicy"
+
+ def __init__(self):
+ self.appHostApp = None
+
+ def OnConnection(self, application, connectMode, addin, custom):
+ print("OnConnection", application, connectMode, addin, custom)
+ try:
+ self.appHostApp = application
+ cbcMyBar = self.appHostApp.CommandBars.Add(Name="PythonBar", Position=constants.msoBarTop, MenuBar=constants.msoBarTypeNormal, Temporary=True)
+ btnMyButton = cbcMyBar.Controls.Add(Type=constants.msoControlButton, Parameter="Greetings")
+ btnMyButton=self.toolbarButton = DispatchWithEvents(btnMyButton, ButtonEvent)
+ btnMyButton.Style = constants.msoButtonCaption
+ btnMyButton.BeginGroup = True
+ btnMyButton.Caption = "&Python"
+ btnMyButton.TooltipText = "Python rules the World"
+ btnMyButton.Width = "34"
+ cbcMyBar.Visible = True
+ except pythoncom.com_error as xxx_todo_changeme:
+ (hr, msg, exc, arg) = xxx_todo_changeme.args
+ print("The Excel call failed with code %d: %s" % (hr, msg))
+ if exc is None:
+ print("There is no extended error information")
+ else:
+ wcode, source, text, helpFile, helpId, scode = exc
+ print("The source of the error is", source)
+ print("The error message is", text)
+ print("More info can be found in %s (id=%d)" % (helpFile, helpId))
+
+ def OnDisconnection(self, mode, custom):
+ print("OnDisconnection")
+ self.appHostApp.CommandBars("PythonBar").Delete
+ self.appHostApp=None
+
+ def OnAddInsUpdate(self, custom):
+ print("OnAddInsUpdate", custom)
+ def OnStartupComplete(self, custom):
+ print("OnStartupComplete", custom)
+ def OnBeginShutdown(self, custom):
+ print("OnBeginShutdown", custom)
+
+def RegisterAddin(klass):
+ import winreg
+ key = winreg.CreateKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Office\\Excel\\Addins")
+ subkey = winreg.CreateKey(key, klass._reg_progid_)
+ winreg.SetValueEx(subkey, "CommandLineSafe", 0, winreg.REG_DWORD, 0)
+ winreg.SetValueEx(subkey, "LoadBehavior", 0, winreg.REG_DWORD, 3)
+ winreg.SetValueEx(subkey, "Description", 0, winreg.REG_SZ, "Excel Addin")
+ winreg.SetValueEx(subkey, "FriendlyName", 0, winreg.REG_SZ, "A Simple Excel Addin")
+
+def UnregisterAddin(klass):
+ import winreg
+ try:
+ winreg.DeleteKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Office\\Excel\\Addins\\" + klass._reg_progid_)
+ except WindowsError:
+ pass
+
+if __name__ == '__main__':
+ import win32com.server.register
+ win32com.server.register.UseCommandLine(ExcelAddin)
+ if "--unregister" in sys.argv:
+ UnregisterAddin(ExcelAddin)
+ else:
+ RegisterAddin(ExcelAddin)
diff --git a/Lib/site-packages/win32com/demos/excelRTDServer.py b/Lib/site-packages/win32com/demos/excelRTDServer.py
new file mode 100644
index 0000000..164b569
--- /dev/null
+++ b/Lib/site-packages/win32com/demos/excelRTDServer.py
@@ -0,0 +1,409 @@
+"""Excel IRTDServer implementation.
+
+This module is a functional example of how to implement the IRTDServer interface
+in python, using the pywin32 extensions. Further details, about this interface
+and it can be found at:
+ http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnexcl2k2/html/odc_xlrtdfaq.asp
+"""
+
+# Copyright (c) 2003-2004 by Chris Nilsson
+#
+# By obtaining, using, and/or copying this software and/or its
+# associated documentation, you agree that you have read, understood,
+# and will comply with the following terms and conditions:
+#
+# Permission to use, copy, modify, and distribute this software and
+# its associated documentation for any purpose and without fee is
+# hereby granted, provided that the above copyright notice appears in
+# all copies, and that both that copyright notice and this permission
+# notice appear in supporting documentation, and that the name of
+# Christopher Nilsson (the author) not be used in advertising or publicity
+# pertaining to distribution of the software without specific, written
+# prior permission.
+#
+# THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
+# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
+# ABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
+# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+
+import pythoncom
+import win32com.client
+from win32com import universal
+from win32com.client import gencache
+from win32com.server.exception import COMException
+
+import threading
+import datetime # For the example classes...
+
+# Typelib info for version 10 - aka Excel XP.
+# This is the minimum version of excel that we can work with as this is when
+# Microsoft introduced these interfaces.
+EXCEL_TLB_GUID = '{00020813-0000-0000-C000-000000000046}'
+EXCEL_TLB_LCID = 0
+EXCEL_TLB_MAJOR = 1
+EXCEL_TLB_MINOR = 4
+
+# Import the excel typelib to make sure we've got early-binding going on.
+# The "ByRef" parameters we use later won't work without this.
+gencache.EnsureModule(EXCEL_TLB_GUID, EXCEL_TLB_LCID, \
+ EXCEL_TLB_MAJOR, EXCEL_TLB_MINOR)
+
+# Tell pywin to import these extra interfaces.
+# --
+# QUESTION: Why? The interfaces seem to descend from IDispatch, so
+# I'd have thought, for example, calling callback.UpdateNotify() (on the
+# IRTDUpdateEvent callback excel gives us) would work without molestation.
+# But the callback needs to be cast to a "real" IRTDUpdateEvent type. Hmm...
+# This is where my small knowledge of the pywin framework / COM gets hazy.
+# --
+# Again, we feed in the Excel typelib as the source of these interfaces.
+universal.RegisterInterfaces(EXCEL_TLB_GUID,
+ EXCEL_TLB_LCID, EXCEL_TLB_MAJOR, EXCEL_TLB_MINOR,
+ ['IRtdServer','IRTDUpdateEvent'])
+
+class ExcelRTDServer(object):
+ """Base RTDServer class.
+
+ Provides most of the features needed to implement the IRtdServer interface.
+ Manages topic adding, removal, and packing up the values for excel.
+
+ Shouldn't be instanciated directly.
+
+ Instead, descendant classes should override the CreateTopic() method.
+ Topic objects only need to provide a GetValue() function to play nice here.
+ The values given need to be atomic (eg. string, int, float... etc).
+
+ Also note: nothing has been done within this class to ensure that we get
+ time to check our topics for updates. I've left that up to the subclass
+ since the ways, and needs, of refreshing your topics will vary greatly. For
+ example, the sample implementation uses a timer thread to wake itself up.
+ Whichever way you choose to do it, your class needs to be able to wake up
+ occaisionally, since excel will never call your class without being asked to
+ first.
+
+ Excel will communicate with our object in this order:
+ 1. Excel instanciates our object and calls ServerStart, providing us with
+ an IRTDUpdateEvent callback object.
+ 2. Excel calls ConnectData when it wants to subscribe to a new "topic".
+ 3. When we have new data to provide, we call the UpdateNotify method of the
+ callback object we were given.
+ 4. Excel calls our RefreshData method, and receives a 2d SafeArray (row-major)
+ containing the Topic ids in the 1st dim, and the topic values in the
+ 2nd dim.
+ 5. When not needed anymore, Excel will call our DisconnectData to
+ unsubscribe from a topic.
+ 6. When there are no more topics left, Excel will call our ServerTerminate
+ method to kill us.
+
+ Throughout, at undetermined periods, Excel will call our Heartbeat
+ method to see if we're still alive. It must return a non-zero value, or
+ we'll be killed.
+
+ NOTE: By default, excel will at most call RefreshData once every 2 seconds.
+ This is a setting that needs to be changed excel-side. To change this,
+ you can set the throttle interval like this in the excel VBA object model:
+ Application.RTD.ThrottleInterval = 1000 ' milliseconds
+ """
+ _com_interfaces_ = ['IRtdServer']
+ _public_methods_ = ['ConnectData','DisconnectData','Heartbeat',
+ 'RefreshData','ServerStart','ServerTerminate']
+ _reg_clsctx_ = pythoncom.CLSCTX_INPROC_SERVER
+ #_reg_clsid_ = "# subclass must provide this class attribute"
+ #_reg_desc_ = "# subclass should provide this description"
+ #_reg_progid_ = "# subclass must provide this class attribute"
+
+ ALIVE = 1
+ NOT_ALIVE = 0
+
+ def __init__(self):
+ """Constructor"""
+ super(ExcelRTDServer, self).__init__()
+ self.IsAlive = self.ALIVE
+ self.__callback = None
+ self.topics = {}
+
+ def SignalExcel(self):
+ """Use the callback we were given to tell excel new data is available."""
+ if self.__callback is None:
+ raise COMException(desc="Callback excel provided is Null")
+ self.__callback.UpdateNotify()
+
+ def ConnectData(self, TopicID, Strings, GetNewValues):
+ """Creates a new topic out of the Strings excel gives us."""
+ try:
+ self.topics[TopicID] = self.CreateTopic(Strings)
+ except Exception as why:
+ raise COMException(desc=str(why))
+ GetNewValues = True
+ result = self.topics[TopicID]
+ if result is None:
+ result = "# %s: Waiting for update" % self.__class__.__name__
+ else:
+ result = result.GetValue()
+
+ # fire out internal event...
+ self.OnConnectData(TopicID)
+
+ # GetNewValues as per interface is ByRef, so we need to pass it back too.
+ return result, GetNewValues
+
+ def DisconnectData(self, TopicID):
+ """Deletes the given topic."""
+ self.OnDisconnectData(TopicID)
+
+ if TopicID in self.topics:
+ self.topics[TopicID] = None
+ del self.topics[TopicID]
+
+ def Heartbeat(self):
+ """Called by excel to see if we're still here."""
+ return self.IsAlive
+
+ def RefreshData(self, TopicCount):
+ """Packs up the topic values. Called by excel when it's ready for an update.
+
+ Needs to:
+ * Return the current number of topics, via the "ByRef" TopicCount
+ * Return a 2d SafeArray of the topic data.
+ - 1st dim: topic numbers
+ - 2nd dim: topic values
+
+ We could do some caching, instead of repacking everytime...
+ But this works for demonstration purposes."""
+ TopicCount = len(self.topics)
+ self.OnRefreshData()
+
+ # Grow the lists, so we don't need a heap of calls to append()
+ results = [[None] * TopicCount, [None] * TopicCount]
+
+ # Excel expects a 2-dimensional array. The first dim contains the
+ # topic numbers, and the second contains the values for the topics.
+ # In true VBA style (yuck), we need to pack the array in row-major format,
+ # which looks like:
+ # ( (topic_num1, topic_num2, ..., topic_numN), \
+ # (topic_val1, topic_val2, ..., topic_valN) )
+ for idx, topicdata in enumerate(self.topics.items()):
+ topicNum, topic = topicdata
+ results[0][idx] = topicNum
+ results[1][idx] = topic.GetValue()
+
+ # TopicCount is meant to be passed to us ByRef, so return it as well, as per
+ # the way pywin32 handles ByRef arguments.
+ return tuple(results), TopicCount
+
+ def ServerStart(self, CallbackObject):
+ """Excel has just created us... We take its callback for later, and set up shop."""
+ self.IsAlive = self.ALIVE
+
+ if CallbackObject is None:
+ raise COMException(desc='Excel did not provide a callback')
+
+ # Need to "cast" the raw PyIDispatch object to the IRTDUpdateEvent interface
+ IRTDUpdateEventKlass = win32com.client.CLSIDToClass.GetClass('{A43788C1-D91B-11D3-8F39-00C04F3651B8}')
+ self.__callback = IRTDUpdateEventKlass(CallbackObject)
+
+ self.OnServerStart()
+
+ return self.IsAlive
+
+ def ServerTerminate(self):
+ """Called when excel no longer wants us."""
+ self.IsAlive = self.NOT_ALIVE # On next heartbeat, excel will free us
+ self.OnServerTerminate()
+
+ def CreateTopic(self, TopicStrings=None):
+ """Topic factory method. Subclass must override.
+
+ Topic objects need to provide:
+ * GetValue() method which returns an atomic value.
+
+ Will raise NotImplemented if not overridden.
+ """
+ raise NotImplemented('Subclass must implement')
+
+ # Overridable class events...
+ def OnConnectData(self, TopicID):
+ """Called when a new topic has been created, at excel's request."""
+ pass
+ def OnDisconnectData(self, TopicID):
+ """Called when a topic is about to be deleted, at excel's request."""
+ pass
+ def OnRefreshData(self):
+ """Called when excel has requested all current topic data."""
+ pass
+ def OnServerStart(self):
+ """Called when excel has instanciated us."""
+ pass
+ def OnServerTerminate(self):
+ """Called when excel is about to destroy us."""
+ pass
+
+class RTDTopic(object):
+ """Base RTD Topic.
+ Only method required by our RTDServer implementation is GetValue().
+ The others are more for convenience."""
+ def __init__(self, TopicStrings):
+ super(RTDTopic, self).__init__()
+ self.TopicStrings = TopicStrings
+ self.__currentValue = None
+ self.__dirty = False
+
+ def Update(self, sender):
+ """Called by the RTD Server.
+ Gives us a chance to check if our topic data needs to be
+ changed (eg. check a file, quiz a database, etc)."""
+ raise NotImplemented('subclass must implement')
+
+ def Reset(self):
+ """Call when this topic isn't considered "dirty" anymore."""
+ self.__dirty = False
+
+ def GetValue(self):
+ return self.__currentValue
+
+ def SetValue(self, value):
+ self.__dirty = True
+ self.__currentValue = value
+
+ def HasChanged(self):
+ return self.__dirty
+
+# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+######################################
+# Example classes
+######################################
+
+class TimeServer(ExcelRTDServer):
+ """Example Time RTD server.
+
+ Sends time updates back to excel.
+
+ example of use, in an excel sheet:
+ =RTD("Python.RTD.TimeServer","","seconds","5")
+
+ This will cause a timestamp string to fill the cell, and update its value
+ every 5 seconds (or as close as possible depending on how busy excel is).
+
+ The empty string parameter denotes the com server is running on the local
+ machine. Otherwise, put in the hostname to look on. For more info
+ on this, lookup the Excel help for its "RTD" worksheet function.
+
+ Obviously, you'd want to wrap this kind of thing in a friendlier VBA
+ function.
+
+ Also, remember that the RTD function accepts a maximum of 28 arguments!
+ If you want to pass more, you may need to concatenate arguments into one
+ string, and have your topic parse them appropriately.
+ """
+ # win32com.server setup attributes...
+ # Never copy the _reg_clsid_ value in your own classes!
+ _reg_clsid_ = '{EA7F2CF1-11A2-45E4-B2D5-68E240DB8CB1}'
+ _reg_progid_ = 'Python.RTD.TimeServer'
+ _reg_desc_ = "Python class implementing Excel IRTDServer -- feeds time"
+
+ # other class attributes...
+ INTERVAL = 0.5 # secs. Threaded timer will wake us up at this interval.
+
+ def __init__(self):
+ super(TimeServer, self).__init__()
+
+ # Simply timer thread to ensure we get to update our topics, and
+ # tell excel about any changes. This is a pretty basic and dirty way to
+ # do this. Ideally, there should be some sort of waitable (eg. either win32
+ # event, socket data event...) and be kicked off by that event triggering.
+ # As soon as we set up shop here, we _must_ return control back to excel.
+ # (ie. we can't block and do our own thing...)
+ self.ticker = threading.Timer(self.INTERVAL, self.Update)
+
+ def OnServerStart(self):
+ self.ticker.start()
+
+ def OnServerTerminate(self):
+ if not self.ticker.finished.isSet():
+ self.ticker.cancel() # Cancel our wake-up thread. Excel has killed us.
+
+ def Update(self):
+ # Get our wake-up thread ready...
+ self.ticker = threading.Timer(self.INTERVAL, self.Update)
+ try:
+ # Check if any of our topics have new info to pass on
+ if len(self.topics):
+ refresh = False
+ for topic in self.topics.values():
+ topic.Update(self)
+ if topic.HasChanged():
+ refresh = True
+ topic.Reset()
+
+ if refresh:
+ self.SignalExcel()
+ finally:
+ self.ticker.start() # Make sure we get to run again
+
+ def CreateTopic(self, TopicStrings=None):
+ """Topic factory. Builds a TimeTopic object out of the given TopicStrings."""
+ return TimeTopic(TopicStrings)
+
+class TimeTopic(RTDTopic):
+ """Example topic for example RTD server.
+
+ Will accept some simple commands to alter how long to delay value updates.
+
+ Commands:
+ * seconds, delay_in_seconds
+ * minutes, delay_in_minutes
+ * hours, delay_in_hours
+ """
+ def __init__(self, TopicStrings):
+ super(TimeTopic, self).__init__(TopicStrings)
+ try:
+ self.cmd, self.delay = self.TopicStrings
+ except Exception as E:
+ # We could simply return a "# ERROR" type string as the
+ # topic value, but explosions like this should be able to get handled by
+ # the VBA-side "On Error" stuff.
+ raise ValueError("Invalid topic strings: %s" % str(TopicStrings))
+
+ #self.cmd = str(self.cmd)
+ self.delay = float(self.delay)
+
+ # setup our initial value
+ self.checkpoint = self.timestamp()
+ self.SetValue(str(self.checkpoint))
+
+ def timestamp(self):
+ return datetime.datetime.now()
+
+ def Update(self, sender):
+ now = self.timestamp()
+ delta = now - self.checkpoint
+ refresh = False
+ if self.cmd == "seconds":
+ if delta.seconds >= self.delay:
+ refresh = True
+ elif self.cmd == "minutes":
+ if delta.minutes >= self.delay:
+ refresh = True
+ elif self.cmd == "hours":
+ if delta.hours >= self.delay:
+ refresh = True
+ else:
+ self.SetValue("#Unknown command: " + self.cmd)
+
+ if refresh:
+ self.SetValue(str(now))
+ self.checkpoint = now
+
+if __name__ == "__main__":
+ import win32com.server.register
+
+ # Register/Unregister TimeServer example
+ # eg. at the command line: excelrtd.py --register
+ # Then type in an excel cell something like:
+ # =RTD("Python.RTD.TimeServer","","seconds","5")
+ win32com.server.register.UseCommandLine(TimeServer)
diff --git a/Lib/site-packages/win32com/demos/iebutton.py b/Lib/site-packages/win32com/demos/iebutton.py
new file mode 100644
index 0000000..4153964
--- /dev/null
+++ b/Lib/site-packages/win32com/demos/iebutton.py
@@ -0,0 +1,188 @@
+# -*- coding: latin-1 -*-
+
+# PyWin32 Internet Explorer Button
+#
+# written by Leonard Ritter (paniq@gmx.net)
+# and Robert Förtsch (info@robert-foertsch.com)
+
+
+"""
+This sample implements a simple IE Button COM server
+with access to the IWebBrowser2 interface.
+
+To demonstrate:
+* Execute this script to register the server.
+* Open Pythonwin's Tools -> Trace Collector Debugging Tool, so you can
+ see the output of 'print' statements in this demo.
+* Open a new IE instance. The toolbar should have a new "scissors" icon,
+ with tooltip text "IE Button" - this is our new button - click it.
+* Switch back to the Pythonwin window - you should see:
+ IOleCommandTarget::Exec called.
+ This is the button being clicked. Extending this to do something more
+ useful is left as an exercise.
+
+Contribtions to this sample to make it a little "friendlier" welcome!
+"""
+
+# imports section
+import sys, os
+from win32com import universal
+from win32com.client import gencache, DispatchWithEvents, Dispatch
+from win32com.client import constants, getevents
+import win32com.server.register
+import win32com
+import pythoncom
+import win32api
+
+# This demo uses 'print' - use win32traceutil to see it if we have no
+# console.
+try:
+ win32api.GetConsoleTitle()
+except win32api.error:
+ import win32traceutil
+
+from win32com.axcontrol import axcontrol
+
+import array, struct
+
+# ensure we know the ms internet controls typelib so we have access to IWebBrowser2 later on
+win32com.client.gencache.EnsureModule('{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}',0,1,1)
+
+
+#
+IObjectWithSite_methods = ['SetSite','GetSite']
+IOleCommandTarget_methods = ['Exec','QueryStatus']
+
+_iebutton_methods_ = IOleCommandTarget_methods + IObjectWithSite_methods
+_iebutton_com_interfaces_ = [
+ axcontrol.IID_IOleCommandTarget,
+ axcontrol.IID_IObjectWithSite, # IObjectWithSite
+]
+
+class Stub:
+ """
+ this class serves as a method stub,
+ outputting debug info whenever the object
+ is being called.
+ """
+
+ def __init__(self,name):
+ self.name = name
+
+ def __call__(self,*args):
+ print('STUB: ',self.name,args)
+
+class IEButton:
+ """
+ The actual COM server class
+ """
+ _com_interfaces_ = _iebutton_com_interfaces_
+ _public_methods_ = _iebutton_methods_
+ _reg_clsctx_ = pythoncom.CLSCTX_INPROC_SERVER
+ _button_text_ = 'IE Button'
+ _tool_tip_ = 'An example implementation for an IE Button.'
+ _icon_ = ''
+ _hot_icon_ = ''
+
+ def __init__( self ):
+ # put stubs for non-implemented methods
+ for method in self._public_methods_:
+ if not hasattr(self,method):
+ print('providing default stub for %s' % method)
+ setattr(self,method,Stub(method))
+
+ def QueryStatus (self, pguidCmdGroup, prgCmds, cmdtextf):
+ # 'cmdtextf' is the 'cmdtextf' element from the OLECMDTEXT structure,
+ # or None if a NULL pointer was passed.
+ result = []
+ for id, flags in prgCmds:
+ flags |= axcontrol.OLECMDF_SUPPORTED | axcontrol.OLECMDF_ENABLED
+ result.append((id, flags))
+ if cmdtextf is None:
+ cmdtext = None # must return None if nothing requested.
+ # IE never seems to want any text - this code is here for
+ # demo purposes only
+ elif cmdtextf == axcontrol.OLECMDTEXTF_NAME:
+ cmdtext = "IEButton Name"
+ else:
+ cmdtext = "IEButton State"
+ return result, cmdtext
+
+ def Exec(self, pguidCmdGroup, nCmdID, nCmdExecOpt, pvaIn):
+ print(pguidCmdGroup, nCmdID, nCmdExecOpt, pvaIn)
+ print("IOleCommandTarget::Exec called.")
+ #self.webbrowser.ShowBrowserBar(GUID_IETOOLBAR, not is_ietoolbar_visible())
+
+ def SetSite(self,unknown):
+ if unknown:
+ # first get a command target
+ cmdtarget = unknown.QueryInterface(axcontrol.IID_IOleCommandTarget)
+ # then travel over to a service provider
+ serviceprovider = cmdtarget.QueryInterface(pythoncom.IID_IServiceProvider)
+ # finally ask for the internet explorer application, returned as a dispatch object
+ self.webbrowser = win32com.client.Dispatch(serviceprovider.QueryService('{0002DF05-0000-0000-C000-000000000046}',pythoncom.IID_IDispatch))
+ else:
+ # lose all references
+ self.webbrowser = None
+
+ def GetClassID(self):
+ return self._reg_clsid_
+
+def register(classobj):
+ import winreg
+ subKeyCLSID = "SOFTWARE\\Microsoft\\Internet Explorer\\Extensions\\%38s" % classobj._reg_clsid_
+ try:
+ hKey = winreg.CreateKey( winreg.HKEY_LOCAL_MACHINE, subKeyCLSID )
+ subKey = winreg.SetValueEx( hKey, "ButtonText", 0, winreg.REG_SZ, classobj._button_text_ )
+ winreg.SetValueEx( hKey, "ClsidExtension", 0, winreg.REG_SZ, classobj._reg_clsid_ ) # reg value for calling COM object
+ winreg.SetValueEx( hKey, "CLSID", 0, winreg.REG_SZ, "{1FBA04EE-3024-11D2-8F1F-0000F87ABD16}" ) # CLSID for button that sends command to COM object
+ winreg.SetValueEx( hKey, "Default Visible", 0, winreg.REG_SZ, "Yes" )
+ winreg.SetValueEx( hKey, "ToolTip", 0, winreg.REG_SZ, classobj._tool_tip_ )
+ winreg.SetValueEx( hKey, "Icon", 0, winreg.REG_SZ, classobj._icon_)
+ winreg.SetValueEx( hKey, "HotIcon", 0, winreg.REG_SZ, classobj._hot_icon_)
+ except WindowsError:
+ print("Couldn't set standard toolbar reg keys.")
+ else:
+ print("Set standard toolbar reg keys.")
+
+def unregister(classobj):
+ import winreg
+ subKeyCLSID = "SOFTWARE\\Microsoft\\Internet Explorer\\Extensions\\%38s" % classobj._reg_clsid_
+ try:
+ hKey = winreg.CreateKey( winreg.HKEY_LOCAL_MACHINE, subKeyCLSID )
+ subKey = winreg.DeleteValue( hKey, "ButtonText" )
+ winreg.DeleteValue( hKey, "ClsidExtension" ) # for calling COM object
+ winreg.DeleteValue( hKey, "CLSID" )
+ winreg.DeleteValue( hKey, "Default Visible" )
+ winreg.DeleteValue( hKey, "ToolTip" )
+ winreg.DeleteValue( hKey, "Icon" )
+ winreg.DeleteValue( hKey, "HotIcon" )
+ winreg.DeleteKey( winreg.HKEY_LOCAL_MACHINE, subKeyCLSID )
+ except WindowsError:
+ print("Couldn't delete Standard toolbar regkey.")
+ else:
+ print("Deleted Standard toolbar regkey.")
+
+#
+# test implementation
+#
+
+class PyWin32InternetExplorerButton(IEButton):
+ _reg_clsid_ = "{104B66A9-9E68-49D1-A3F5-94754BE9E0E6}"
+ _reg_progid_ = "PyWin32.IEButton"
+ _reg_desc_ = 'Test Button'
+ _button_text_ = 'IE Button'
+ _tool_tip_ = 'An example implementation for an IE Button.'
+ _icon_ = ''
+ _hot_icon_ = _icon_
+
+def DllRegisterServer():
+ register(PyWin32InternetExplorerButton)
+
+def DllUnregisterServer():
+ unregister(PyWin32InternetExplorerButton)
+
+if __name__ == '__main__':
+ win32com.server.register.UseCommandLine(PyWin32InternetExplorerButton,
+ finalize_register = DllRegisterServer,
+ finalize_unregister = DllUnregisterServer)
diff --git a/Lib/site-packages/win32com/demos/ietoolbar.py b/Lib/site-packages/win32com/demos/ietoolbar.py
new file mode 100644
index 0000000..3a204be
--- /dev/null
+++ b/Lib/site-packages/win32com/demos/ietoolbar.py
@@ -0,0 +1,320 @@
+# -*- coding: latin-1 -*-
+
+# PyWin32 Internet Explorer Toolbar
+#
+# written by Leonard Ritter (paniq@gmx.net)
+# and Robert Förtsch (info@robert-foertsch.com)
+
+
+"""
+This sample implements a simple IE Toolbar COM server
+supporting Windows XP styles and access to
+the IWebBrowser2 interface.
+
+It also demonstrates how to hijack the parent window
+to catch WM_COMMAND messages.
+"""
+
+# imports section
+import sys, os
+from win32com import universal
+from win32com.client import gencache, DispatchWithEvents, Dispatch
+from win32com.client import constants, getevents
+import win32com
+import pythoncom
+import winreg
+
+from win32com.shell import shell
+from win32com.shell.shellcon import *
+from win32com.axcontrol import axcontrol
+
+try:
+ # try to get styles (winxp)
+ import winxpgui as win32gui
+except:
+ # import default module (win2k and lower)
+ import win32gui
+import win32ui
+import win32con
+import commctrl
+
+import array, struct
+
+# ensure we know the ms internet controls typelib so we have access to IWebBrowser2 later on
+win32com.client.gencache.EnsureModule('{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}',0,1,1)
+
+#
+IDeskBand_methods = ['GetBandInfo']
+IDockingWindow_methods = ['ShowDW','CloseDW','ResizeBorderDW']
+IOleWindow_methods = ['GetWindow','ContextSensitiveHelp']
+IInputObject_methods = ['UIActivateIO','HasFocusIO','TranslateAcceleratorIO']
+IObjectWithSite_methods = ['SetSite','GetSite']
+IPersistStream_methods = ['GetClassID','IsDirty','Load','Save','GetSizeMax']
+
+_ietoolbar_methods_ = IDeskBand_methods + IDockingWindow_methods + \
+ IOleWindow_methods + IInputObject_methods + \
+ IObjectWithSite_methods + IPersistStream_methods
+_ietoolbar_com_interfaces_ = [
+ shell.IID_IDeskBand, # IDeskBand
+ axcontrol.IID_IObjectWithSite, # IObjectWithSite
+ pythoncom.IID_IPersistStream,
+ axcontrol.IID_IOleCommandTarget,
+]
+
+class WIN32STRUCT:
+ def __init__(self, **kw):
+ full_fmt = ""
+ for name, fmt, default in self._struct_items_:
+ self.__dict__[name] = None
+ if fmt == "z":
+ full_fmt += "pi"
+ else:
+ full_fmt += fmt
+ for name, val in kw.items():
+ self.__dict__[name] = val
+
+ def __setattr__(self, attr, val):
+ if not attr.startswith("_") and attr not in self.__dict__:
+ raise AttributeError(attr)
+ self.__dict__[attr] = val
+
+ def toparam(self):
+ self._buffs = []
+ full_fmt = ""
+ vals = []
+ for name, fmt, default in self._struct_items_:
+ val = self.__dict__[name]
+ if fmt == "z":
+ fmt = "Pi"
+ if val is None:
+ vals.append(0)
+ vals.append(0)
+ else:
+ str_buf = array.array("c", val+'\0')
+ vals.append(str_buf.buffer_info()[0])
+ vals.append(len(val))
+ self._buffs.append(str_buf) # keep alive during the call.
+ else:
+ if val is None:
+ val = default
+ vals.append(val)
+ full_fmt += fmt
+ return struct.pack(*(full_fmt,) + tuple(vals))
+
+class TBBUTTON(WIN32STRUCT):
+ _struct_items_ = [
+ ("iBitmap", "i", 0),
+ ("idCommand", "i", 0),
+ ("fsState", "B", 0),
+ ("fsStyle", "B", 0),
+ ("bReserved", "H", 0),
+ ("dwData", "I", 0),
+ ("iString", "z", None),
+ ]
+
+class Stub:
+ """
+ this class serves as a method stub,
+ outputting debug info whenever the object
+ is being called.
+ """
+
+ def __init__(self,name):
+ self.name = name
+
+ def __call__(self,*args):
+ print('STUB: ',self.name,args)
+
+class IEToolbarCtrl:
+ """
+ a tiny wrapper for our winapi-based
+ toolbar control implementation.
+ """
+ def __init__(self,hwndparent):
+ styles = win32con.WS_CHILD \
+ | win32con.WS_VISIBLE \
+ | win32con.WS_CLIPSIBLINGS \
+ | win32con.WS_CLIPCHILDREN \
+ | commctrl.TBSTYLE_LIST \
+ | commctrl.TBSTYLE_FLAT \
+ | commctrl.TBSTYLE_TRANSPARENT \
+ | commctrl.CCS_TOP \
+ | commctrl.CCS_NODIVIDER \
+ | commctrl.CCS_NORESIZE \
+ | commctrl.CCS_NOPARENTALIGN
+ self.hwnd = win32gui.CreateWindow('ToolbarWindow32', None, styles,
+ 0, 0, 100, 100,
+ hwndparent, 0, win32gui.dllhandle,
+ None)
+ win32gui.SendMessage(self.hwnd, commctrl.TB_BUTTONSTRUCTSIZE, 20, 0)
+
+ def ShowWindow(self,mode):
+ win32gui.ShowWindow(self.hwnd,mode)
+
+ def AddButtons(self,*buttons):
+ tbbuttons = ''
+ for button in buttons:
+ tbbuttons += button.toparam()
+ return win32gui.SendMessage(self.hwnd, commctrl.TB_ADDBUTTONS,
+ len(buttons), tbbuttons)
+
+ def GetSafeHwnd(self):
+ return self.hwnd
+
+class IEToolbar:
+ """
+ The actual COM server class
+ """
+ _com_interfaces_ = _ietoolbar_com_interfaces_
+ _public_methods_ = _ietoolbar_methods_
+ _reg_clsctx_ = pythoncom.CLSCTX_INPROC_SERVER
+ # if you copy and modify this example, be sure to change the clsid below
+ _reg_clsid_ = "{F21202A2-959A-4149-B1C3-68B9013F3335}"
+ _reg_progid_ = "PyWin32.IEToolbar"
+ _reg_desc_ = 'PyWin32 IE Toolbar'
+
+ def __init__( self ):
+ # put stubs for non-implemented methods
+ for method in self._public_methods_:
+ if not hasattr(self,method):
+ print('providing default stub for %s' % method)
+ setattr(self,method,Stub(method))
+
+ def GetWindow(self):
+ return self.toolbar.GetSafeHwnd()
+
+ def Load(self, stream):
+ # called when the toolbar is loaded
+ pass
+
+ def Save(self, pStream, fClearDirty):
+ # called when the toolbar shall save its information
+ pass
+
+ def CloseDW(self, dwReserved):
+ del self.toolbar
+
+ def ShowDW(self, bShow):
+ if bShow:
+ self.toolbar.ShowWindow(win32con.SW_SHOW)
+ else:
+ self.toolbar.ShowWindow(win32con.SW_HIDE)
+
+ def on_first_button(self):
+ print("first!")
+ self.webbrowser.Navigate2('http://starship.python.net/crew/mhammond/')
+
+ def on_second_button(self):
+ print("second!")
+
+ def on_third_button(self):
+ print("third!")
+
+ def toolbar_command_handler(self,args):
+ hwnd,message,wparam,lparam,time,point = args
+ if lparam == self.toolbar.GetSafeHwnd():
+ self._command_map[wparam]()
+
+ def SetSite(self,unknown):
+ if unknown:
+ # retrieve the parent window interface for this site
+ olewindow = unknown.QueryInterface(pythoncom.IID_IOleWindow)
+ # ask the window for its handle
+ hwndparent = olewindow.GetWindow()
+
+ # first get a command target
+ cmdtarget = unknown.QueryInterface(axcontrol.IID_IOleCommandTarget)
+ # then travel over to a service provider
+ serviceprovider = cmdtarget.QueryInterface(pythoncom.IID_IServiceProvider)
+ # finally ask for the internet explorer application, returned as a dispatch object
+ self.webbrowser = win32com.client.Dispatch(serviceprovider.QueryService('{0002DF05-0000-0000-C000-000000000046}',pythoncom.IID_IDispatch))
+
+ # now create and set up the toolbar
+ self.toolbar = IEToolbarCtrl(hwndparent)
+
+ buttons = [
+ ('Visit PyWin32 Homepage',self.on_first_button),
+ ('Another Button', self.on_second_button),
+ ('Yet Another Button', self.on_third_button),
+ ]
+
+ self._command_map = {}
+ # wrap our parent window so we can hook message handlers
+ window = win32ui.CreateWindowFromHandle(hwndparent)
+
+ # add the buttons
+ for i in range(len(buttons)):
+ button = TBBUTTON()
+ name,func = buttons[i]
+ id = 0x4444+i
+ button.iBitmap = -2
+ button.idCommand = id
+ button.fsState = commctrl.TBSTATE_ENABLED
+ button.fsStyle = commctrl.TBSTYLE_BUTTON
+ button.iString = name
+ self._command_map[0x4444+i] = func
+ self.toolbar.AddButtons(button)
+ window.HookMessage(self.toolbar_command_handler,win32con.WM_COMMAND)
+ else:
+ # lose all references
+ self.webbrowser = None
+
+ def GetClassID(self):
+ return self._reg_clsid_
+
+ def GetBandInfo(self, dwBandId, dwViewMode, dwMask):
+ ptMinSize = (0,24)
+ ptMaxSize = (2000,24)
+ ptIntegral = (0,0)
+ ptActual = (2000,24)
+ wszTitle = 'PyWin32 IE Toolbar'
+ dwModeFlags = DBIMF_VARIABLEHEIGHT
+ crBkgnd = 0
+ return (ptMinSize,ptMaxSize,ptIntegral,ptActual,wszTitle,dwModeFlags,crBkgnd)
+
+# used for HKLM install
+def DllInstall( bInstall, cmdLine ):
+ comclass = IEToolbar
+
+# register plugin
+def DllRegisterServer():
+ comclass = IEToolbar
+
+ # register toolbar with IE
+ try:
+ print("Trying to register Toolbar.\n")
+ hkey = winreg.CreateKey( winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Internet Explorer\\Toolbar" )
+ subKey = winreg.SetValueEx( hkey, comclass._reg_clsid_, 0, winreg.REG_BINARY, "\0" )
+ except WindowsError:
+ print("Couldn't set registry value.\nhkey: %d\tCLSID: %s\n" % ( hkey, comclass._reg_clsid_ ))
+ else:
+ print("Set registry value.\nhkey: %d\tCLSID: %s\n" % ( hkey, comclass._reg_clsid_ ))
+ # TODO: implement reg settings for standard toolbar button
+
+# unregister plugin
+def DllUnregisterServer():
+ comclass = IEToolbar
+
+ # unregister toolbar from internet explorer
+ try:
+ print("Trying to unregister Toolbar.\n")
+ hkey = winreg.CreateKey( winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Internet Explorer\\Toolbar" )
+ winreg.DeleteValue( hkey, comclass._reg_clsid_ )
+ except WindowsError:
+ print("Couldn't delete registry value.\nhkey: %d\tCLSID: %s\n" % ( hkey, comclass._reg_clsid_ ))
+ else:
+ print("Deleting reg key succeeded.\n")
+
+# entry point
+if __name__ == '__main__':
+ import win32com.server.register
+ win32com.server.register.UseCommandLine( IEToolbar )
+
+ # parse actual command line option
+ if "--unregister" in sys.argv:
+ DllUnregisterServer()
+ else:
+ DllRegisterServer()
+else:
+ # import trace utility for remote debugging
+ import win32traceutil
diff --git a/Lib/site-packages/win32com/demos/outlookAddin.py b/Lib/site-packages/win32com/demos/outlookAddin.py
new file mode 100644
index 0000000..6d2d24c
--- /dev/null
+++ b/Lib/site-packages/win32com/demos/outlookAddin.py
@@ -0,0 +1,114 @@
+# A demo plugin for Microsoft Outlook (NOT Outlook Express)
+#
+# This addin simply adds a new button to the main Outlook toolbar,
+# and displays a message box when clicked. Thus, it demonstrates
+# how to plug in to Outlook itself, and hook outlook events.
+#
+# Additionally, each time a new message arrives in the Inbox, a message
+# is printed with the subject of the message.
+#
+# To register the addin, simply execute:
+# outlookAddin.py
+# This will install the COM server, and write the necessary
+# AddIn key to Outlook
+#
+# To unregister completely:
+# outlookAddin.py --unregister
+#
+# To debug, execute:
+# outlookAddin.py --debug
+#
+# Then open Pythonwin, and select "Tools->Trace Collector Debugging Tool"
+# Restart Outlook, and you should see some output generated.
+#
+# NOTE: If the AddIn fails with an error, Outlook will re-register
+# the addin to not automatically load next time Outlook starts. To
+# correct this, simply re-register the addin (see above)
+
+from win32com import universal
+from win32com.server.exception import COMException
+from win32com.client import gencache, DispatchWithEvents
+import winerror
+import pythoncom
+from win32com.client import constants
+import sys
+
+# Support for COM objects we use.
+gencache.EnsureModule('{00062FFF-0000-0000-C000-000000000046}', 0, 9, 0, bForDemand=True) # Outlook 9
+gencache.EnsureModule('{2DF8D04C-5BFA-101B-BDE5-00AA0044DE52}', 0, 2, 1, bForDemand=True) # Office 9
+
+# The TLB defiining the interfaces we implement
+universal.RegisterInterfaces('{AC0714F2-3D04-11D1-AE7D-00A0C90F26F4}', 0, 1, 0, ["_IDTExtensibility2"])
+
+class ButtonEvent:
+ def OnClick(self, button, cancel):
+ import win32ui # Possible, but not necessary, to use a Pythonwin GUI
+ win32ui.MessageBox("Hello from Python")
+ return cancel
+
+class FolderEvent:
+ def OnItemAdd(self, item):
+ try:
+ print("An item was added to the inbox with subject:", item.Subject)
+ except AttributeError:
+ print("An item was added to the inbox, but it has no subject! - ", repr(item))
+
+
+
+class OutlookAddin:
+ _com_interfaces_ = ['_IDTExtensibility2']
+ _public_methods_ = []
+ _reg_clsctx_ = pythoncom.CLSCTX_INPROC_SERVER
+ _reg_clsid_ = "{0F47D9F3-598B-4d24-B7E3-92AC15ED27E2}"
+ _reg_progid_ = "Python.Test.OutlookAddin"
+ _reg_policy_spec_ = "win32com.server.policy.EventHandlerPolicy"
+ def OnConnection(self, application, connectMode, addin, custom):
+ print("OnConnection", application, connectMode, addin, custom)
+ # ActiveExplorer may be none when started without a UI (eg, WinCE synchronisation)
+ activeExplorer = application.ActiveExplorer()
+ if activeExplorer is not None:
+ bars = activeExplorer.CommandBars
+ toolbar = bars.Item("Standard")
+ item = toolbar.Controls.Add(Type=constants.msoControlButton, Temporary=True)
+ # Hook events for the item
+ item = self.toolbarButton = DispatchWithEvents(item, ButtonEvent)
+ item.Caption="Python"
+ item.TooltipText = "Click for Python"
+ item.Enabled = True
+
+ # And now, for the sake of demonstration, setup a hook for all new messages
+ inbox = application.Session.GetDefaultFolder(constants.olFolderInbox)
+ self.inboxItems = DispatchWithEvents(inbox.Items, FolderEvent)
+
+ def OnDisconnection(self, mode, custom):
+ print("OnDisconnection")
+ def OnAddInsUpdate(self, custom):
+ print("OnAddInsUpdate", custom)
+ def OnStartupComplete(self, custom):
+ print("OnStartupComplete", custom)
+ def OnBeginShutdown(self, custom):
+ print("OnBeginShutdown", custom)
+
+def RegisterAddin(klass):
+ import winreg
+ key = winreg.CreateKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Office\\Outlook\\Addins")
+ subkey = winreg.CreateKey(key, klass._reg_progid_)
+ winreg.SetValueEx(subkey, "CommandLineSafe", 0, winreg.REG_DWORD, 0)
+ winreg.SetValueEx(subkey, "LoadBehavior", 0, winreg.REG_DWORD, 3)
+ winreg.SetValueEx(subkey, "Description", 0, winreg.REG_SZ, klass._reg_progid_)
+ winreg.SetValueEx(subkey, "FriendlyName", 0, winreg.REG_SZ, klass._reg_progid_)
+
+def UnregisterAddin(klass):
+ import winreg
+ try:
+ winreg.DeleteKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Office\\Outlook\\Addins\\" + klass._reg_progid_)
+ except WindowsError:
+ pass
+
+if __name__ == '__main__':
+ import win32com.server.register
+ win32com.server.register.UseCommandLine(OutlookAddin)
+ if "--unregister" in sys.argv:
+ UnregisterAddin(OutlookAddin)
+ else:
+ RegisterAddin(OutlookAddin)
diff --git a/Lib/site-packages/win32com/demos/trybag.py b/Lib/site-packages/win32com/demos/trybag.py
new file mode 100644
index 0000000..1e95f59
--- /dev/null
+++ b/Lib/site-packages/win32com/demos/trybag.py
@@ -0,0 +1,75 @@
+import pythoncom
+from win32com.server import util
+from win32com.server import exception
+
+VT_EMPTY = pythoncom.VT_EMPTY
+
+class Bag:
+ _public_methods_ = [ 'Read', 'Write' ]
+ _com_interfaces_ = [ pythoncom.IID_IPropertyBag ]
+
+ def __init__(self):
+ self.data = { }
+
+ def Read(self, propName, varType, errorLog):
+ print("read: name=", propName, "type=", varType)
+ if propName not in self.data:
+ if errorLog:
+ hr = 0x80070057
+ exc = pythoncom.com_error(0, "Bag.Read", "no such item", None, 0, hr)
+ errorLog.AddError(propName, exc)
+ raise exception.Exception(scode=hr)
+ return self.data[propName]
+
+ def Write(self, propName, value):
+ print("write: name=", propName, "value=", value)
+ self.data[propName] = value
+
+
+class Target:
+ _public_methods_ = [ 'GetClassID', 'InitNew', 'Load', 'Save' ]
+ _com_interfaces_ = [ pythoncom.IID_IPersist,
+ pythoncom.IID_IPersistPropertyBag ]
+
+ def GetClassID(self):
+ raise exception.Exception(scode=0x80004005) # E_FAIL
+
+ def InitNew(self):
+ pass
+
+ def Load(self, bag, log):
+ print(bag.Read('prop1', VT_EMPTY, log))
+ print(bag.Read('prop2', VT_EMPTY, log))
+ try:
+ print(bag.Read('prop3', VT_EMPTY, log))
+ except exception.Exception:
+ pass
+
+ def Save(self, bag, clearDirty, saveAllProps):
+ bag.Write('prop1', 'prop1.hello')
+ bag.Write('prop2', 'prop2.there')
+
+class Log:
+ _public_methods_ = [ 'AddError' ]
+ _com_interfaces_ = [ pythoncom.IID_IErrorLog ]
+
+ def AddError(self, propName, excepInfo):
+ print("error: propName=", propName, "error=", excepInfo)
+
+def test():
+ bag = Bag()
+ target = Target()
+ log = Log()
+
+ target.Save(bag, 1, 1)
+ target.Load(bag, log)
+
+ comBag = util.wrap(bag, pythoncom.IID_IPropertyBag)
+ comTarget = util.wrap(target, pythoncom.IID_IPersistPropertyBag)
+ comLog = util.wrap(log, pythoncom.IID_IErrorLog)
+
+ comTarget.Save(comBag, 1, 1)
+ comTarget.Load(comBag, comLog)
+
+if __name__ == '__main__':
+ test()
diff --git a/Lib/site-packages/win32com/include/PythonCOM.h b/Lib/site-packages/win32com/include/PythonCOM.h
new file mode 100644
index 0000000..6a9c0e7
--- /dev/null
+++ b/Lib/site-packages/win32com/include/PythonCOM.h
@@ -0,0 +1,787 @@
+/* PythonCOM.h
+
+ Main header for Python COM support.
+
+ This file is involved mainly with client side COM support for
+ Python.
+
+ Most COM work put together by Greg Stein and Mark Hammond, with a
+ few others starting to come out of the closet.
+
+
+ --------------------------------------------------------------------
+ Thread State Rules
+ ------------------
+ These rules apply to PythonCOM in general, and not just to
+ the client side.
+
+ The rules are quite simple, but it is critical they be followed.
+ In general, errors here will be picked up quite quickly, as Python
+ will raise a Fatal Error. However, the Release() issue in particular
+ may keep a number of problems well hidden.
+
+ Interfaces:
+ -----------
+ Before making ANY call out to COM, you MUST release the Python lock.
+ This is true to ANY call whatsoever, including the COM call in question,
+ but also any calls to "->Release();"
+
+ This is normally achieved with the calls
+ PY_INTERFACE_PRECALL and PY_INTERFACE_POSTCALL, which release
+ and acquire the Python lock.
+
+ Gateways:
+ ---------
+ Before doing anything related to Python, gateways MUST acquire the
+ Python lock, and must release it before returning.
+
+ This is normally achieved with PY_GATEWAY_METHOD at the top of a
+ gateway method. This macro resolves to a class, which automatically does
+ the right thing.
+
+ Release:
+ --------
+ As mentioned above for Interfaces, EVERY call to Release() must be done
+ with the Python lock released. This is expanded here.
+
+ This is very important, but an error may not be noticed. The problem will
+ only be seen when the Release() is on a Python object and the Release() is the
+ final one for the object. In this case, the Python object will attempt to
+ acquire the Python lock before destroying itself, and Python will raise a
+ fatal error.
+
+ In many many cases, you will not notice this error, but someday, someone will
+ implement the other side in Python, and suddenly FatalErrors will start
+ appearing. Make sure you get this right.
+
+ Eg, this code is correct:
+ PY_INTERFACE_PRECALL;
+ pSomeObj->SomeFunction(pSomeOtherObject);
+ pSomeOtherObject->Release();
+ PY_INTERFACE_POSTCALL;
+
+ However, this code is WRONG, but will RARELY FAIL.
+ PY_INTERFACE_PRECALL;
+ pSomeObj->SomeFunction(pSomeOtherObject);
+ PY_INTERFACE_POSTCALL;
+ pSomeOtherObject->Release();
+--------------------------------------------------------------------
+*/
+#ifndef __PYTHONCOM_H__
+#define __PYTHONCOM_H__
+
+// #define _DEBUG_LIFETIMES // Trace COM object lifetimes.
+
+#ifdef FREEZE_PYTHONCOM
+ /* The pythoncom module is being included in a frozen .EXE/.DLL */
+# define PYCOM_EXPORT
+#else
+# ifdef BUILD_PYTHONCOM
+ /* We are building pythoncomxx.dll */
+# define PYCOM_EXPORT __declspec(dllexport)
+# else
+ /* This module uses pythoncomxx.dll */
+# define PYCOM_EXPORT __declspec(dllimport)
+# ifndef _DEBUG
+# pragma comment(lib, "pythoncom.lib")
+# else
+# pragma comment(lib, "pythoncom_d.lib")
+# endif
+# endif
+#endif
+
+#ifdef MS_WINCE
+// List of interfaces not supported by CE.
+#define NO_PYCOM_IDISPATCHEX
+#define NO_PYCOM_IPROVIDECLASSINFO
+#define NO_PYCOM_IENUMGUID
+#define NO_PYCOM_IENUMCATEGORYINFO
+#define NO_PYCOM_ICATINFORMATION
+#define NO_PYCOM_ICATREGISTER
+#define NO_PYCOM_ISERVICEPROVIDER
+#define NO_PYCOM_IPROPERTYSTORAGE
+#define NO_PYCOM_IPROPERTYSETSTORAGE
+#define NO_PYCOM_ENUMSTATPROPSTG
+
+#include "ocidl.h"
+#include "oleauto.h"
+
+#endif // MS_WINCE
+
+#ifdef __MINGW32__
+// Special Mingw32 considerations.
+#define NO_PYCOM_IDISPATCHEX
+#define NO_PYCOM_IPROVIDECLASSINFO
+#define NO_PYCOM_ISERVICEPROVIDER
+#define NO_PYCOM_ENUMSTATPROPSTG
+#define NO_PYCOM_IPROPERTYSTORAGE
+#define __try try
+#define __except catch
+#include
+
+#endif // __MINGW32__
+
+#include // Standard Win32 Types
+
+#ifndef NO_PYCOM_IDISPATCHEX
+#include // New header for IDispatchEx interface.
+#endif // NO_PYCOM_IDISPATCHEX
+
+#if defined(MAINWIN)
+// Mainwin seems to have 1/2 the VT_RECORD infrastructure in place
+# if !defined(VT_RECORD)
+# define VT_RECORD 36
+# define V_RECORDINFO(X) ((X)->brecVal.pRecInfo)
+# define V_RECORD(X) ((X)->brecVal.pvRecord)
+# else
+# pragma message("MAINWIN appears to have grown correct VT_RECORD " \
+ "support. Please update PythonCOM.h accordingly")
+# endif //VT_RECORD
+#endif // MAINWIN
+
+class PyIUnknown;
+// To make life interesting/complicated, I use C++ classes for
+// all Python objects. The main advantage is that I can derive
+// a PyIDispatch object from a PyIUnknown, etc. This provides a
+// clean C++ interface, and "automatically" provides all base
+// Python methods to "derived" Python types.
+//
+// Main disadvantage is that any extension DLLs will need to include
+// these headers, and link with this .lib
+//
+// Base class for (most of) the type objects.
+
+class PYCOM_EXPORT PyComTypeObject : public PyTypeObject {
+public:
+ PyComTypeObject( const char *name, PyComTypeObject *pBaseType, int typeSize, struct PyMethodDef* methodList, PyIUnknown* (* thector)(IUnknown *) );
+ ~PyComTypeObject();
+
+ // is the given object an interface type object? (e.g. PyIUnknown)
+ static BOOL is_interface_type(PyObject *ob);
+
+public:
+ PyIUnknown * (* ctor)(IUnknown *);
+};
+
+// A type used for interfaces that can automatically provide enumerators
+// (ie, they themselves aren't enumerable, but do have a suitable default
+// method that returns a PyIEnum object
+class PYCOM_EXPORT PyComEnumProviderTypeObject : public PyComTypeObject {
+public:
+ PyComEnumProviderTypeObject( const char *name,
+ PyComTypeObject *pBaseType,
+ int typeSize,
+ struct PyMethodDef* methodList,
+ PyIUnknown* (* thector)(IUnknown *),
+ const char *enum_method_name);
+ static PyObject *iter(PyObject *self);
+ const char *enum_method_name;
+};
+
+// A type used for PyIEnum interfaces
+class PYCOM_EXPORT PyComEnumTypeObject : public PyComTypeObject {
+public:
+ static PyObject *iter(PyObject *self);
+ static PyObject *iternext(PyObject *self);
+ PyComEnumTypeObject( const char *name, PyComTypeObject *pBaseType, int typeSize, struct PyMethodDef* methodList, PyIUnknown* (* thector)(IUnknown *) );
+};
+
+// Very very base class - not COM specific - Should exist in the
+// Python core somewhere, IMO.
+class PYCOM_EXPORT PyIBase :
+ public PyObject
+{
+public:
+ // virtuals for Python support
+ virtual PyObject *getattr(char *name);
+ virtual int setattr(char *name, PyObject *v);
+ virtual PyObject *repr();
+ virtual int compare(PyObject *other) {
+ if (this == other)
+ return 0;
+ if (this < other)
+ return -1;
+ return 1;
+ }
+ // These iter are a little special, in that returning NULL means
+ // use the implementation in the type
+ virtual PyObject *iter() {return NULL;}
+ virtual PyObject *iternext() {return NULL;}
+protected:
+ PyIBase();
+ virtual ~PyIBase();
+
+public:
+ static BOOL is_object(PyObject *, PyComTypeObject *which);
+ BOOL is_object(PyComTypeObject *which);
+ static void dealloc(PyObject *ob);
+ static PyObject *repr(PyObject *ob);
+ static PyObject *getattro(PyObject *self, PyObject *name);
+ static int setattro(PyObject *op, PyObject *obname, PyObject *v);
+ static int cmp(PyObject *ob1, PyObject *ob2);
+ static PyObject *richcmp(PyObject *ob1, PyObject *ob2, int op);
+
+};
+
+/* Special Type objects */
+extern PYCOM_EXPORT PyTypeObject PyOleEmptyType; // equivalent to VT_EMPTY
+extern PYCOM_EXPORT PyTypeObject PyOleMissingType; // special Python handling.
+extern PYCOM_EXPORT PyTypeObject PyOleArgNotFoundType; // special VT_ERROR value
+extern PYCOM_EXPORT PyTypeObject PyOleNothingType; // special VT_ERROR value
+
+// ALL of these set an appropriate Python error on bad return.
+
+// Given a Python object that is a registered COM type, return a given
+// interface pointer on its underlying object, with a new reference added.
+PYCOM_EXPORT BOOL PyCom_InterfaceFromPyObject(
+ PyObject *ob,
+ REFIID iid,
+ LPVOID *ppv,
+ BOOL bNoneOK=TRUE
+ );
+
+// As above, but allows instance with "_oleobj_" attribute.
+PYCOM_EXPORT BOOL PyCom_InterfaceFromPyInstanceOrObject(
+ PyObject *ob,
+ REFIID iid,
+ LPVOID *ppv,
+ BOOL bNoneOK=TRUE
+ );
+
+// Release an arbitary COM pointer.
+// NOTE: the PRECALL/POSTCALL stuff is probably not strictly necessary
+// since the PyGILSTATE stuff has been in place (and even then, it only
+// mattered when it was the last Release() on a Python implemented object)
+#define PYCOM_RELEASE(pUnk) { \
+ if (pUnk) { \
+ PY_INTERFACE_PRECALL; \
+ (pUnk)->Release(); \
+ PY_INTERFACE_POSTCALL; \
+ } \
+}
+
+// Given an IUnknown and an Interface ID, create and return an object
+// of the appropriate type. eg IID_Unknown->PyIUnknown,
+// IID_IDispatch->PyIDispatch, etc.
+// Uses a map that external extension DLLs can populate with their IID/type.
+// Under the principal of least surprise, this will return Py_None is punk is NULL.
+// Otherwise, a valid PyI*, but with NULL m_obj (and therefore totally useless)
+// object would be created.
+// BOOL bAddRef indicates if a COM reference count should be added to the IUnknown.
+// This depends purely on the context in which it is called. If the IUnknown is obtained
+// from a function that creates a new ref (eg, CoCreateInstance()) then you should use
+// FALSE. If you receive the pointer as (eg) a param to a gateway function, then
+// you normally need to pass TRUE, as this is truly a new reference.
+// *** ALWAYS take the time to get this right. ***
+PYCOM_EXPORT PyObject *PyCom_PyObjectFromIUnknown(IUnknown *punk, REFIID riid, BOOL bAddRef = FALSE);
+
+// VARIANT <-> PyObject conversion utilities.
+PYCOM_EXPORT BOOL PyCom_VariantFromPyObject(PyObject *obj, VARIANT *var);
+PYCOM_EXPORT PyObject *PyCom_PyObjectFromVariant(const VARIANT *var);
+
+// PROPVARIANT
+PYCOM_EXPORT PyObject *PyObject_FromPROPVARIANT( PROPVARIANT *pVar );
+PYCOM_EXPORT PyObject *PyObject_FromPROPVARIANTs( PROPVARIANT *pVars, ULONG cVars );
+PYCOM_EXPORT BOOL PyObject_AsPROPVARIANT(PyObject *ob, PROPVARIANT *pVar);
+
+// Other conversion helpers...
+PYCOM_EXPORT PyObject *PyCom_PyObjectFromSTATSTG(STATSTG *pStat);
+PYCOM_EXPORT BOOL PyCom_PyObjectAsSTATSTG(PyObject *ob, STATSTG *pStat, DWORD flags = 0);
+PYCOM_EXPORT BOOL PyCom_SAFEARRAYFromPyObject(PyObject *obj, SAFEARRAY **ppSA, VARENUM vt = VT_VARIANT);
+PYCOM_EXPORT PyObject *PyCom_PyObjectFromSAFEARRAY(SAFEARRAY *psa, VARENUM vt = VT_VARIANT );
+#ifndef NO_PYCOM_STGOPTIONS
+PYCOM_EXPORT BOOL PyCom_PyObjectAsSTGOPTIONS(PyObject *obstgoptions, STGOPTIONS **ppstgoptions);
+#endif
+PYCOM_EXPORT PyObject *PyCom_PyObjectFromSTATPROPSETSTG(STATPROPSETSTG *pStat);
+PYCOM_EXPORT BOOL PyCom_PyObjectAsSTATPROPSETSTG(PyObject *, STATPROPSETSTG *);
+
+// Currency support.
+PYCOM_EXPORT PyObject *PyObject_FromCurrency(CURRENCY &cy);
+PYCOM_EXPORT BOOL PyObject_AsCurrency(PyObject *ob, CURRENCY *pcy);
+
+// OLEMENUGROUPWIDTHS are used by axcontrol, shell, etc
+PYCOM_EXPORT BOOL PyObject_AsOLEMENUGROUPWIDTHS( PyObject *oblpMenuWidths, OLEMENUGROUPWIDTHS *pWidths);
+PYCOM_EXPORT PyObject *PyObject_FromOLEMENUGROUPWIDTHS(const OLEMENUGROUPWIDTHS *pWidths);
+
+/* Functions for Initializing COM, and also letting the core know about it!
+*/
+PYCOM_EXPORT HRESULT PyCom_CoInitializeEx(LPVOID reserved, DWORD dwInit);
+PYCOM_EXPORT HRESULT PyCom_CoInitialize(LPVOID reserved);
+PYCOM_EXPORT void PyCom_CoUninitialize();
+
+///////////////////////////////////////////////////////////////////
+// Error related functions
+
+// Client related functions - generally called by interfaces before
+// they return NULL back to Python to indicate the error.
+// All these functions return NULL so interfaces can generally
+// just "return PyCom_BuildPyException(hr, punk, IID_IWhatever)"
+
+// Uses the HRESULT, and IErrorInfo interfaces if available to
+// create and set a pythoncom.com_error.
+PYCOM_EXPORT PyObject *PyCom_BuildPyException(HRESULT hr, IUnknown *pUnk=NULL, REFIID iid=IID_NULL);
+
+// Uses the HRESULT and an EXCEPINFO structure to create and
+// set a pythoncom.com_error.
+PYCOM_EXPORT PyObject* PyCom_BuildPyExceptionFromEXCEPINFO(HRESULT hr, EXCEPINFO *pexcepInfo, UINT nArgErr = (UINT)-1);
+
+// Sets a pythoncom.internal_error - no one should ever see these!
+PYCOM_EXPORT PyObject* PyCom_BuildInternalPyException(char *msg);
+
+// Log an error to a Python logger object if one can be found, or
+// to stderr if no log available.
+// If logProvider is not NULL, we will call a "_GetLogger_()" method on it.
+// If logProvider is NULL, we attempt to fetch "win32com.logger".
+// If they do not exist, return None, or raise an error fetching them
+// (or even writing to them once fetched), the message still goes to stderr.
+// NOTE: By default, win32com does *not* provide a logger, so default is that
+// all errors are written to stdout.
+// This will *not* write a record if a COM Server error is current.
+PYCOM_EXPORT void PyCom_LoggerNonServerException(PyObject *logProvider,
+ const char *fmt, ...);
+
+// Write an error record, including exception. This will write an error
+// record even if a COM server error is current.
+PYCOM_EXPORT void PyCom_LoggerException(PyObject *logProvider, const char *fmt, ...);
+
+// Write a warning record - in general this does *not* mean a call failed, but
+// still is something in the programmers control that they should change.
+// XXX - if an exception is pending when this is called, the traceback will
+// also be written. This is undesirable and will be changed should this
+// start being a problem.
+PYCOM_EXPORT void PyCom_LoggerWarning(PyObject *logProvider, const char *fmt, ...);
+
+// Server related error functions
+// These are supplied so that any Python errors we detect can be
+// converted into COM error information. The HRESULT returned should
+// be returned by the COM function, and these functions also set the
+// IErrorInfo interfaces, so the caller can extract more detailed
+// information about the Python exception.
+
+// Set a COM exception, logging the exception if not an explicitly raised 'server' exception
+PYCOM_EXPORT HRESULT PyCom_SetAndLogCOMErrorFromPyException(const char *methodName, REFIID riid /* = IID_NULL */);
+PYCOM_EXPORT HRESULT PyCom_SetAndLogCOMErrorFromPyExceptionEx(PyObject *provider, const char *methodName, REFIID riid /* = IID_NULL */);
+
+// Used in gateways to SetErrorInfo() with a simple HRESULT, then return it.
+// The description is generally only useful for debugging purposes,
+// and if you are debugging via a server that supports IErrorInfo (like Python :-)
+// NOTE: this function is usuable from outside the Python context
+PYCOM_EXPORT HRESULT PyCom_SetCOMErrorFromSimple(HRESULT hr, REFIID riid = IID_NULL, const char *description = NULL);
+
+// Used in gateways to SetErrorInfo() the current Python exception, and
+// (assuming not a server error explicitly raised) also logs an error
+// to stdout/win32com.logger.
+// NOTE: this function assumes GIL held
+PYCOM_EXPORT HRESULT PyCom_SetCOMErrorFromPyException(REFIID riid = IID_NULL);
+
+// A couple of EXCEPINFO helpers - could be private to IDispatch
+// if it wasnt for the AXScript support (and ITypeInfo if we get around to that :-)
+// These functions do not set any error states to either Python or
+// COM - they simply convert to/from PyObjects and EXCEPINFOs
+
+// Use the current Python exception to fill an EXCEPINFO structure.
+PYCOM_EXPORT void PyCom_ExcepInfoFromPyException(EXCEPINFO *pExcepInfo);
+
+// Fill in an EXCEPINFO structure from a Python instance or tuple object.
+// (ie, similar to the above, except the Python exception object is specified,
+// rather than using the "current"
+PYCOM_EXPORT BOOL PyCom_ExcepInfoFromPyObject(PyObject *obExcepInfo, EXCEPINFO *pexcepInfo, HRESULT *phresult = NULL);
+
+// Create a Python object holding the exception information. The exception
+// information is *not* freed by this function. Python exceptions are
+// raised and NULL is returned if an error occurs.
+PYCOM_EXPORT PyObject *PyCom_PyObjectFromExcepInfo(const EXCEPINFO *pexcepInfo);
+
+///////////////////////////////////////////////////////////////////
+//
+// External C++ helpers - these helpers are for other DLLs which
+// may need similar functionality, but dont want to duplicate all
+
+// This helper is for an application that has an IDispatch, and COM arguments
+// and wants to call a Python function. It is assumed the caller can map the IDispatch
+// to a Python object, so the Python handler is passed.
+// Args:
+// handler : A Python callable object.
+// dispparms : the COM arguments.
+// pVarResult : The variant for the return value of the Python call.
+// pexcepinfo : Exception info the helper may fill out.
+// puArgErr : Argument error the helper may fill out on exception
+// addnArgs : Any additional arguments to the Python function. May be NULL.
+// If addnArgs is NULL, then it is assumed the Python call should be native -
+// ie, the COM args are packed as normal Python args to the call.
+// If addnArgs is NOT NULL, it is assumed the Python function itself is
+// a helper. This Python function will be called with 2 arguments - both
+// tuples - first one is the COM args, second is the addn args.
+PYCOM_EXPORT BOOL PyCom_MakeOlePythonCall(PyObject *handler, DISPPARAMS FAR* params, VARIANT FAR* pVarResult,
+ EXCEPINFO FAR* pexcepinfo, UINT FAR* puArgErr, PyObject *addnlArgs);
+
+/////////////////////////////////////////////////////////////////////////////
+// Various special purpose singletons
+class PYCOM_EXPORT PyOleEmpty : public PyObject
+{
+public:
+ PyOleEmpty();
+};
+
+class PYCOM_EXPORT PyOleMissing : public PyObject
+{
+public:
+ PyOleMissing();
+};
+
+class PYCOM_EXPORT PyOleArgNotFound : public PyObject
+{
+public:
+ PyOleArgNotFound();
+};
+
+class PYCOM_EXPORT PyOleNothing : public PyObject
+{
+public:
+ PyOleNothing();
+};
+
+
+// We need to dynamically create C++ Python objects
+// These helpers allow each type object to create it.
+#define MAKE_PYCOM_CTOR(classname) static PyIUnknown * classname::PyObConstruct(IUnknown *pInitObj) {return new classname(pInitObj);}
+#define MAKE_PYCOM_CTOR_ERRORINFO(classname, iid) \
+ static PyIUnknown * classname::PyObConstruct(IUnknown *pInitObj) {return new classname(pInitObj);} \
+ static PyObject *SetPythonCOMError(PyObject *self, HRESULT hr) {return PyCom_BuildPyException(hr, GetI(self), iid);}
+#define GET_PYCOM_CTOR(classname) classname::PyObConstruct
+
+// Macros that interfaces should use. PY_INTERFACE_METHOD at the top of the method
+// The other 2 wrap directly around the underlying method call.
+#define PY_INTERFACE_METHOD
+// Identical to Py_BEGIN_ALLOW_THREADS except no { !!!
+#define PY_INTERFACE_PRECALL PyThreadState *_save = PyEval_SaveThread();
+#define PY_INTERFACE_POSTCALL PyEval_RestoreThread(_save);
+
+/////////////////////////////////////////////////////////////////////////////
+// class PyIUnknown
+class PYCOM_EXPORT PyIUnknown : public PyIBase
+{
+public:
+ MAKE_PYCOM_CTOR(PyIUnknown);
+ virtual PyObject *repr();
+ virtual int compare(PyObject *other);
+
+ static IUnknown *GetI(PyObject *self);
+ IUnknown *m_obj;
+ static char *szErrMsgObjectReleased;
+ static void SafeRelease(PyIUnknown *ob);
+ static PyComTypeObject type;
+
+ // The Python methods
+ static PyObject *QueryInterface(PyObject *self, PyObject *args);
+ static PyObject *SafeRelease(PyObject *self, PyObject *args);
+
+protected:
+ PyIUnknown(IUnknown *punk);
+ ~PyIUnknown();
+};
+
+/////////////////////////////////////////////////////////////////////////////
+// class PyIDispatch
+
+class PYCOM_EXPORT PyIDispatch : public PyIUnknown
+{
+public:
+ MAKE_PYCOM_CTOR(PyIDispatch);
+ static IDispatch *GetI(PyObject *self);
+ static PyComTypeObject type;
+
+ // The Python methods
+ static PyObject *Invoke(PyObject *self, PyObject *args);
+ static PyObject *InvokeTypes(PyObject *self, PyObject *args);
+ static PyObject *GetIDsOfNames(PyObject *self, PyObject *args);
+ static PyObject *GetTypeInfo(PyObject *self, PyObject *args);
+ static PyObject *GetTypeInfoCount(PyObject *self, PyObject *args);
+protected:
+ PyIDispatch(IUnknown *pdisp);
+ ~PyIDispatch();
+};
+
+#ifndef NO_PYCOM_IDISPATCHEX
+/////////////////////////////////////////////////////////////////////////////
+// class PyIDispatchEx
+
+class PYCOM_EXPORT PyIDispatchEx : public PyIDispatch
+{
+public:
+ MAKE_PYCOM_CTOR_ERRORINFO(PyIDispatchEx, IID_IDispatchEx);
+ static IDispatchEx *GetI(PyObject *self);
+ static PyComTypeObject type;
+
+ // The Python methods
+ static PyObject *GetDispID(PyObject *self, PyObject *args);
+ static PyObject *InvokeEx(PyObject *self, PyObject *args);
+ static PyObject *DeleteMemberByName(PyObject *self, PyObject *args);
+ static PyObject *DeleteMemberByDispID(PyObject *self, PyObject *args);
+ static PyObject *GetMemberProperties(PyObject *self, PyObject *args);
+ static PyObject *GetMemberName(PyObject *self, PyObject *args);
+ static PyObject *GetNextDispID(PyObject *self, PyObject *args);
+
+protected:
+ PyIDispatchEx(IUnknown *pdisp);
+ ~PyIDispatchEx();
+};
+#endif // NO_PYCOM_IDISPATCHEX
+
+/////////////////////////////////////////////////////////////////////////////
+// class PyIClassFactory
+
+class PYCOM_EXPORT PyIClassFactory : public PyIUnknown
+{
+public:
+ MAKE_PYCOM_CTOR(PyIClassFactory);
+ static IClassFactory *GetI(PyObject *self);
+ static PyComTypeObject type;
+
+ // The Python methods
+ static PyObject *CreateInstance(PyObject *self, PyObject *args);
+ static PyObject *LockServer(PyObject *self, PyObject *args);
+protected:
+ PyIClassFactory(IUnknown *pdisp);
+ ~PyIClassFactory();
+};
+
+#ifndef NO_PYCOM_IPROVIDECLASSINFO
+
+/////////////////////////////////////////////////////////////////////////////
+// class PyIProvideTypeInfo
+
+class PYCOM_EXPORT PyIProvideClassInfo : public PyIUnknown
+{
+public:
+ MAKE_PYCOM_CTOR(PyIProvideClassInfo);
+ static IProvideClassInfo *GetI(PyObject *self);
+ static PyComTypeObject type;
+
+ // The Python methods
+ static PyObject *GetClassInfo(PyObject *self, PyObject *args);
+protected:
+ PyIProvideClassInfo(IUnknown *pdisp);
+ ~PyIProvideClassInfo();
+};
+
+class PYCOM_EXPORT PyIProvideClassInfo2 : public PyIProvideClassInfo
+{
+public:
+ MAKE_PYCOM_CTOR(PyIProvideClassInfo2);
+ static IProvideClassInfo2 *GetI(PyObject *self);
+ static PyComTypeObject type;
+
+ // The Python methods
+ static PyObject *GetGUID(PyObject *self, PyObject *args);
+protected:
+ PyIProvideClassInfo2(IUnknown *pdisp);
+ ~PyIProvideClassInfo2();
+};
+#endif //NO_PYCOM_IPROVIDECLASSINFO
+
+/////////////////////////////////////////////////////////////////////////////
+// class PyITypeInfo
+class PYCOM_EXPORT PyITypeInfo : public PyIUnknown
+{
+public:
+ MAKE_PYCOM_CTOR(PyITypeInfo);
+ static PyComTypeObject type;
+ static ITypeInfo *GetI(PyObject *self);
+
+ PyObject *GetContainingTypeLib();
+ PyObject *GetDocumentation(MEMBERID);
+ PyObject *GetRefTypeInfo(HREFTYPE href);
+ PyObject *GetRefTypeOfImplType(int index);
+ PyObject *GetFuncDesc(int pos);
+ PyObject *GetIDsOfNames(OLECHAR FAR* FAR*, int);
+ PyObject *GetNames(MEMBERID);
+ PyObject *GetTypeAttr();
+ PyObject *GetVarDesc(int pos);
+ PyObject *GetImplTypeFlags(int index);
+ PyObject *GetTypeComp();
+
+protected:
+ PyITypeInfo(IUnknown *);
+ ~PyITypeInfo();
+};
+
+/////////////////////////////////////////////////////////////////////////////
+// class PyITypeComp
+class PYCOM_EXPORT PyITypeComp : public PyIUnknown
+{
+public:
+ MAKE_PYCOM_CTOR(PyITypeComp);
+ static PyComTypeObject type;
+ static ITypeComp *GetI(PyObject *self);
+
+ PyObject *Bind(OLECHAR* szName, unsigned short wflags);
+ PyObject *BindType(OLECHAR* szName);
+
+protected:
+ PyITypeComp(IUnknown *);
+ ~PyITypeComp();
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// class CPyTypeLib
+
+class PYCOM_EXPORT PyITypeLib : public PyIUnknown
+{
+public:
+ MAKE_PYCOM_CTOR(PyITypeLib);
+ static PyComTypeObject type;
+ static ITypeLib *GetI(PyObject *self);
+
+ PyObject *GetLibAttr();
+ PyObject *GetDocumentation(int pos);
+ PyObject *GetTypeInfo(int pos);
+ PyObject *GetTypeInfoCount();
+ PyObject *GetTypeInfoOfGuid(REFGUID guid);
+ PyObject *GetTypeInfoType(int pos);
+ PyObject *GetTypeComp();
+
+protected:
+ PyITypeLib(IUnknown *);
+ ~PyITypeLib();
+};
+
+/////////////////////////////////////////////////////////////////////////////
+// class PyIConnectionPoint
+
+class PYCOM_EXPORT PyIConnectionPoint : public PyIUnknown
+{
+public:
+ MAKE_PYCOM_CTOR_ERRORINFO(PyIConnectionPoint,IID_IConnectionPoint);
+ static PyComTypeObject type;
+ static IConnectionPoint *GetI(PyObject *self);
+
+ static PyObject *GetConnectionInterface(PyObject *self, PyObject *args);
+ static PyObject *GetConnectionPointContainer(PyObject *self, PyObject *args);
+ static PyObject *Advise(PyObject *self, PyObject *args);
+ static PyObject *Unadvise(PyObject *self, PyObject *args);
+ static PyObject *EnumConnections(PyObject *self, PyObject *args);
+
+protected:
+ PyIConnectionPoint(IUnknown *);
+ ~PyIConnectionPoint();
+};
+
+class PYCOM_EXPORT PyIConnectionPointContainer : public PyIUnknown
+{
+public:
+ MAKE_PYCOM_CTOR_ERRORINFO(PyIConnectionPointContainer, IID_IConnectionPointContainer);
+ static PyComTypeObject type;
+ static IConnectionPointContainer *GetI(PyObject *self);
+
+ static PyObject *EnumConnectionPoints(PyObject *self, PyObject *args);
+ static PyObject *FindConnectionPoint(PyObject *self, PyObject *args);
+
+protected:
+ PyIConnectionPointContainer(IUnknown *);
+ ~PyIConnectionPointContainer();
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// class PythonOleArgHelper
+//
+// A PythonOleArgHelper is used primarily to help out Python helpers
+// which need to convert from a Python object when the specific OLE
+// type is known - eg, when a TypeInfo is available.
+//
+// The type of conversion determines who owns what buffers etc. I wish BYREF didnt exist :-)
+typedef enum {
+ // We dont know what sort of conversion it is yet.
+ POAH_CONVERT_UNKNOWN,
+ // A PyObject is given, we convert to a VARIANT, make the COM call, then BYREFs back to a PyObject
+ // ie, this is typically a "normal" COM call, where Python initiates the call
+ POAH_CONVERT_FROM_PYOBJECT,
+ // A VARIANT is given, we convert to a PyObject, make the Python call, then BYREFs back to a VARIANT.
+ // ie, this is typically handling a COM event, where COM itself initiates the call.
+ POAH_CONVERT_FROM_VARIANT,
+} POAH_CONVERT_DIRECTION;
+
+class PYCOM_EXPORT PythonOleArgHelper
+{
+public:
+ PythonOleArgHelper();
+ ~PythonOleArgHelper();
+ BOOL ParseTypeInformation(PyObject *reqdObjectTuple);
+
+ // Using this call with reqdObject != NULL will check the existing
+ // VT_ of the variant. If not VT_EMPTY, then the result will be coerced to
+ // that type. This contrasts with PyCom_PyObjectToVariant which just
+ // uses the Python type to determine the variant type.
+ BOOL MakeObjToVariant(PyObject *obj, VARIANT *var, PyObject *reqdObjectTuple = NULL);
+ PyObject *MakeVariantToObj(VARIANT *var);
+
+ VARTYPE m_reqdType;
+ BOOL m_bParsedTypeInfo;
+ BOOL m_bIsOut;
+ POAH_CONVERT_DIRECTION m_convertDirection;
+ PyObject *m_pyVariant; // if non-null, a win32com.client.VARIANT
+ union {
+ void *m_pValueHolder;
+ short m_sBuf;
+ long m_lBuf;
+ LONGLONG m_llBuf;
+ VARIANT_BOOL m_boolBuf;
+ double m_dBuf;
+ float m_fBuf;
+ IDispatch *m_dispBuf;
+ IUnknown *m_unkBuf;
+ SAFEARRAY *m_arrayBuf;
+ VARIANT *m_varBuf;
+ DATE m_dateBuf;
+ CY m_cyBuf;
+ };
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// global functions and variables
+PYCOM_EXPORT BOOL MakePythonArgumentTuples(PyObject **pArgs, PythonOleArgHelper **ppHelpers,
+ PyObject **pNamedArgs, PythonOleArgHelper **ppNamedHelpers,
+ DISPPARAMS FAR* params);
+
+
+// Convert a Python object to a BSTR - allow embedded NULLs, None, etc.
+PYCOM_EXPORT BOOL PyCom_BstrFromPyObject(PyObject *stringObject, BSTR *pResult, BOOL bNoneOK = FALSE);
+
+// MakeBstrToObj - convert a BSTR into a Python string.
+//
+// ONLY USE THIS FOR TRUE BSTR's - Use the fn below for OLECHAR *'s.
+// NOTE - does not use standard macros, so NULLs get through!
+PYCOM_EXPORT PyObject *MakeBstrToObj(const BSTR bstr);
+
+// Size info is available (eg, a fn returns a string and also fills in a size variable)
+PYCOM_EXPORT PyObject *MakeOLECHARToObj(const OLECHAR * str, int numChars);
+
+// No size info avail.
+PYCOM_EXPORT PyObject *MakeOLECHARToObj(const OLECHAR * str);
+
+PYCOM_EXPORT void PyCom_LogF(const char *fmt, ...);
+
+// Generic conversion from python sequence to VT_VECTOR array
+// Resulting array must be freed with CoTaskMemFree
+template
+BOOL SeqToVector(PyObject *ob, arraytype **pA, ULONG *pcount, BOOL (*converter)(PyObject *, arraytype *)){
+ TmpPyObject seq = PyWinSequence_Tuple(ob, pcount);
+ if (seq == NULL)
+ return FALSE;
+ *pA = (arraytype *)CoTaskMemAlloc(*pcount * sizeof(arraytype));
+ if (*pA == NULL){
+ PyErr_NoMemory();
+ return FALSE;
+ }
+ for (ULONG i=0; i<*pcount; i++){
+ PyObject *item = PyTuple_GET_ITEM((PyObject *)seq, i);
+ if (!(*converter)(item, &(*pA)[i]))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+#endif // __PYTHONCOM_H__
diff --git a/Lib/site-packages/win32com/include/PythonCOMRegister.h b/Lib/site-packages/win32com/include/PythonCOMRegister.h
new file mode 100644
index 0000000..7dcef13
--- /dev/null
+++ b/Lib/site-packages/win32com/include/PythonCOMRegister.h
@@ -0,0 +1,55 @@
+// Support for PythonCOM and its extensions to register the interfaces,
+// gateways and IIDs it supports.
+//
+// The module can simply declare an array of type PyCom_InterfaceSupportInfo, then
+// use the macros to populate it.
+//
+// See Register.cpp and AXScript.cpp for examples on its use.
+
+#ifndef __PYTHONCOMREGISTER_H__
+#define __PYTHONCOMREGISTER_H__
+
+#include "PythonCOMServer.h" // Need defns in this file...
+
+typedef struct
+{
+ const GUID *pGUID; // The supported IID - required
+ const char *interfaceName; // Name of the interface - required
+ const char *iidName; // Name of the IID that goes into the dict. - required
+ PyTypeObject *pTypeOb; // the type object for client PyI* side - NULL for server only support.
+ pfnPyGatewayConstructor ctor; // Gateway (PyG*) interface constructor - NULL for client only support
+
+} PyCom_InterfaceSupportInfo;
+
+#define PYCOM_INTERFACE_IID_ONLY(ifc) { &IID_I##ifc, "I" #ifc, "IID_I" #ifc, NULL, NULL }
+#define PYCOM_INTERFACE_CLSID_ONLY(ifc) { &CLSID_##ifc, "CLSID_" #ifc, "CLSID_" #ifc, NULL, NULL }
+#define PYCOM_INTERFACE_CATID_ONLY(ifc) { &CATID_##ifc, "CATID_" #ifc, "CATID_" #ifc, NULL, NULL }
+#define PYCOM_INTERFACE_CLIENT_ONLY(ifc) { &IID_I##ifc, "I" #ifc, "IID_I" #ifc, &PyI##ifc::type, NULL }
+#define PYCOM_INTERFACE_SERVER_ONLY(ifc) { &IID_I##ifc, "I" #ifc, "IID_I" #ifc, NULL, GET_PYGATEWAY_CTOR(PyG##ifc)}
+#define PYCOM_INTERFACE_FULL(ifc) { &IID_I##ifc, "I" #ifc, "IID_I" #ifc, &PyI##ifc::type, GET_PYGATEWAY_CTOR(PyG##ifc)}
+
+// Versions that use __uuidof() to get the IID, which seems to avoid the need
+// to link with a lib holding the IIDs. Note that almost all extensions
+// build with __uuidof() being the default; the build failed at 'shell' - so
+// we could consider making this the default and making the 'explicit' version
+// above the special case.
+#define PYCOM_INTERFACE_IID_ONLY_UUIDOF(ifc) { &__uuidof(I##ifc), "I" #ifc, "IID_I" #ifc, NULL, NULL }
+#define PYCOM_INTERFACE_CLIENT_ONLY_UUIDOF(ifc) { &__uuidof(I##ifc), "I" #ifc, "IID_I" #ifc, &PyI##ifc::type, NULL }
+#define PYCOM_INTERFACE_SERVER_ONLY_UUIDOF(ifc) { &__uuidof(I##ifc), "I" #ifc, "IID_I" #ifc, NULL, GET_PYGATEWAY_CTOR(PyG##ifc)}
+#define PYCOM_INTERFACE_FULL_UUIDOF(ifc) { &__uuidof(I##ifc), "I" #ifc, "IID_I" #ifc, &PyI##ifc::type, GET_PYGATEWAY_CTOR(PyG##ifc)}
+
+// Prototypes for the register functions
+
+// Register a PythonCOM extension module
+PYCOM_EXPORT int PyCom_RegisterExtensionSupport( PyObject *dict, const PyCom_InterfaceSupportInfo *pInterfaces, int numEntries);
+
+// THESE SHOULD NO LONGER BE USED. Instead, use the functions above passing an
+// array of PyCom_InterfaceSupportInfo objects.
+
+PYCOM_EXPORT int PyCom_RegisterClientType(PyTypeObject *typeOb, const GUID *guid);
+
+HRESULT PYCOM_EXPORT PyCom_RegisterGatewayObject(REFIID iid, pfnPyGatewayConstructor ctor, const char *interfaceName);
+PYCOM_EXPORT int PyCom_IsGatewayRegistered(REFIID iid);
+
+
+#endif /* __PYTHONCOMREGISTER_H__ */
diff --git a/Lib/site-packages/win32com/include/PythonCOMServer.h b/Lib/site-packages/win32com/include/PythonCOMServer.h
new file mode 100644
index 0000000..6a50d99
--- /dev/null
+++ b/Lib/site-packages/win32com/include/PythonCOMServer.h
@@ -0,0 +1,162 @@
+#ifndef __PYTHONCOMSERVER_H__
+#define __PYTHONCOMSERVER_H__
+
+// PythonCOMServer.h :Server side COM support
+
+#include
+
+#define DLLAcquireGlobalLock PyWin_AcquireGlobalLock
+#define DLLReleaseGlobalLock PyWin_ReleaseGlobalLock
+
+void PYCOM_EXPORT PyCom_DLLAddRef(void);
+void PYCOM_EXPORT PyCom_DLLReleaseRef(void);
+
+// Use this macro at the start of all gateway methods.
+#define PY_GATEWAY_METHOD CEnterLeavePython _celp
+
+class PyGatewayBase;
+// Gateway constructors.
+// Each gateway must be able to be created from a "gateway constructor". This
+// is simply a function that takes a Python instance as as argument, and returns
+// a gateway object of the correct type. The MAKE_PYGATEWAY_CTOR is a helper that
+// will embed such a constructor in the class - however, this is not necessary -
+// _any_ function of the correct signature can be used.
+
+typedef HRESULT (* pfnPyGatewayConstructor)(PyObject *PythonInstance, PyGatewayBase *, void **ppResult, REFIID iid);
+HRESULT PyCom_MakeRegisteredGatewayObject(REFIID iid, PyObject *instance, PyGatewayBase *base, void **ppv);
+
+// A version of the above which support classes being derived from
+// other than IUnknown
+#define PYGATEWAY_MAKE_SUPPORT2(classname, IInterface, theIID, gatewaybaseclass) \
+ public: \
+ static HRESULT classname::PyGatewayConstruct(PyObject *pPyInstance, PyGatewayBase *unkBase, void **ppResult, REFIID iid) { \
+ if (ppResult==NULL) return E_INVALIDARG; \
+ classname *newob = new classname(pPyInstance); \
+ newob->m_pBaseObject = unkBase; \
+ if (unkBase) unkBase->AddRef(); \
+ *ppResult = newob->ThisAsIID(iid); \
+ return *ppResult ? S_OK : E_OUTOFMEMORY; } \
+ protected: \
+ virtual IID GetIID(void) { return theIID; } \
+ virtual void *ThisAsIID(IID iid) {if (this==NULL) return NULL;if (iid==theIID) return (IInterface *)this; else return gatewaybaseclass::ThisAsIID(iid);} \
+ STDMETHOD_(ULONG,AddRef)(void) {return gatewaybaseclass::AddRef();} \
+ STDMETHOD_(ULONG,Release)(void) {return gatewaybaseclass::Release();} \
+ STDMETHOD(QueryInterface)(REFIID iid, void ** obj) {return gatewaybaseclass::QueryInterface(iid, obj);};
+
+// This is the "old" version to use, or use it if you derive
+// directly from PyGatewayBase
+#define PYGATEWAY_MAKE_SUPPORT(classname, IInterface, theIID) \
+ PYGATEWAY_MAKE_SUPPORT2(classname, IInterface, theIID, PyGatewayBase)
+
+
+#define GET_PYGATEWAY_CTOR(classname) classname::PyGatewayConstruct
+
+#ifdef _MSC_VER
+// Disable an OK warning...
+#pragma warning( disable : 4275 )
+// warning C4275: non dll-interface struct 'IDispatch' used as base for dll-interface class 'PyGatewayBase'
+#endif // _MSC_VER
+
+// Helper interface for fetching a Python object from a gateway
+
+extern const GUID IID_IInternalUnwrapPythonObject;
+
+interface IInternalUnwrapPythonObject : public IUnknown
+{
+public:
+ STDMETHOD(Unwrap)( PyObject **ppPyObject ) = 0;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+// PyGatewayBase
+//
+// Base class for all gateways.
+//
+class PYCOM_EXPORT PyGatewayBase :
+#ifndef NO_PYCOM_IDISPATCHEX
+ public IDispatchEx, // IDispatch comes along for the ride!
+#else
+ public IDispatch, // No IDispatchEx - must explicitely use IDispatch
+#endif
+ public ISupportErrorInfo,
+ public IInternalUnwrapPythonObject
+{
+protected:
+ PyGatewayBase(PyObject *instance);
+ virtual ~PyGatewayBase();
+
+ // Invoke the Python method (via the policy object)
+ STDMETHOD(InvokeViaPolicy)(
+ const char *szMethodName,
+ PyObject **ppResult = NULL,
+ const char *szFormat = NULL,
+ ...);
+
+public:
+ // IUnknown
+ STDMETHOD_(ULONG,AddRef)(void);
+ STDMETHOD_(ULONG,Release)(void);
+ STDMETHOD(QueryInterface)(REFIID iid, void ** obj);
+
+ // IDispatch
+ STDMETHOD(GetTypeInfoCount)(UINT FAR* pctInfo);
+ STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo FAR* FAR* pptInfo);
+ STDMETHOD(GetIDsOfNames)(REFIID refiid, OLECHAR FAR* FAR* rgszNames, UINT cNames, LCID lcid, DISPID FAR* rgdispid);
+ STDMETHOD(Invoke)(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR* params, VARIANT FAR* pVarResult, EXCEPINFO FAR* pexcepinfo, UINT FAR* puArgErr);
+
+ // IDispatchEx
+#ifndef NO_PYCOM_IDISPATCHEX
+ STDMETHOD(GetDispID)(BSTR bstrName, DWORD grfdex, DISPID *pid);
+ STDMETHOD(InvokeEx)(DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller);
+ STDMETHOD(DeleteMemberByName)(BSTR bstr, DWORD grfdex);
+ STDMETHOD(DeleteMemberByDispID)(DISPID id);
+ STDMETHOD(GetMemberProperties)(DISPID id, DWORD grfdexFetch, DWORD *pgrfdex);
+ STDMETHOD(GetMemberName)(DISPID id, BSTR *pbstrName);
+ STDMETHOD(GetNextDispID)(DWORD grfdex, DISPID id, DISPID *pid);
+ STDMETHOD(GetNameSpaceParent)(IUnknown **ppunk);
+#endif // NO_PYCOM_IDISPATCHEX
+ // ISupportErrorInfo
+ STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);
+
+ // IInternalUnwrapPythonObject
+ STDMETHOD(Unwrap)(PyObject **ppPyObject);
+
+ // Basically just PYGATEWAY_MAKE_SUPPORT(PyGatewayBase, IDispatch, IID_IDispatch);
+ // but with special handling as its the base class.
+ static HRESULT PyGatewayBase::PyGatewayConstruct(PyObject *pPyInstance, PyGatewayBase *gatewayBase, void **ppResult, REFIID iid)
+ {
+ if (ppResult==NULL) return E_INVALIDARG;
+ PyGatewayBase *obNew = new PyGatewayBase(pPyInstance);
+ obNew->m_pBaseObject = gatewayBase;
+ if (gatewayBase) gatewayBase->AddRef();
+ *ppResult = (IDispatch *)obNew;
+ return *ppResult ? S_OK : E_OUTOFMEMORY;
+ }
+ // Currently this is used only for ISupportErrorInfo,
+ // so hopefully this will never be called in this base class.
+ // (however, this is not a rule, so we wont assert or anything!)
+ virtual IID GetIID(void) { return IID_IUnknown; }
+ virtual void *ThisAsIID(IID iid);
+ // End of PYGATEWAY_MAKE_SUPPORT
+ PyObject * m_pPyObject;
+ PyGatewayBase *m_pBaseObject;
+private:
+ LONG m_cRef;
+};
+
+#ifdef _MSC_VER
+#pragma warning(default : 4275 )
+#endif // _MSC_VER
+
+// B/W compat hack for gateways.
+#define PyCom_HandlePythonFailureToCOM() \
+ PyCom_SetAndLogCOMErrorFromPyExceptionEx(this->m_pPyObject, "", GetIID())
+
+// F/W compat hack for gateways! Must be careful about updating
+// PyGatewayBase vtable, so a slightly older pythoncomXX.dll will work
+// with slightly later extensions. So use a #define.
+#define MAKE_PYCOM_GATEWAY_FAILURE_CODE(method_name) \
+ PyCom_SetAndLogCOMErrorFromPyExceptionEx(this->m_pPyObject, method_name, GetIID())
+
+
+#endif /* __PYTHONCOMSERVER_H__ */
diff --git a/Lib/site-packages/win32com/libs/axscript.lib b/Lib/site-packages/win32com/libs/axscript.lib
new file mode 100644
index 0000000..3f7e9be
Binary files /dev/null and b/Lib/site-packages/win32com/libs/axscript.lib differ
diff --git a/Lib/site-packages/win32com/libs/pythoncom.lib b/Lib/site-packages/win32com/libs/pythoncom.lib
new file mode 100644
index 0000000..7c1ba3b
Binary files /dev/null and b/Lib/site-packages/win32com/libs/pythoncom.lib differ
diff --git a/Lib/site-packages/win32com/makegw/__init__.py b/Lib/site-packages/win32com/makegw/__init__.py
new file mode 100644
index 0000000..c4927ed
--- /dev/null
+++ b/Lib/site-packages/win32com/makegw/__init__.py
@@ -0,0 +1 @@
+# indicates a python package.
diff --git a/Lib/site-packages/win32com/makegw/makegw.py b/Lib/site-packages/win32com/makegw/makegw.py
new file mode 100644
index 0000000..376d60a
--- /dev/null
+++ b/Lib/site-packages/win32com/makegw/makegw.py
@@ -0,0 +1,451 @@
+"""Utility functions for writing out gateway C++ files
+
+ This module will generate a C++/Python binding for a specific COM
+ interface.
+
+ At this stage, no command line interface exists. You must start Python,
+ import this module, change to the directory where the generated code should
+ be written, and run the public function.
+
+ This module is capable of generating both 'Interfaces' (ie, Python
+ client side support for the interface) and 'Gateways' (ie, Python
+ server side support for the interface). Many COM interfaces are useful
+ both as Client and Server. Other interfaces, however, really only make
+ sense to implement one side or the other. For example, it would be pointless
+ for Python to implement Server side for 'IRunningObjectTable', unless we were
+ implementing core COM for an operating system in Python (hey - now there's an idea!)
+
+ Most COM interface code is totally boiler-plate - it consists of
+ converting arguments, dispatching the call to Python, and processing
+ any result values.
+
+ This module automates the generation of such code. It has the ability to
+ parse a .H file generated by the MIDL tool (ie, almost all COM .h files)
+ and build almost totally complete C++ code.
+
+ The module understands some of the well known data types, and how to
+ convert them. There are only a couple of places where hand-editing is
+ necessary, as detailed below:
+
+ unsupported types -- If a type is not known, the generator will
+ pretty much ignore it, but write a comment to the generated code. You
+ may want to add custom support for this type. In some cases, C++ compile errors
+ will result. These are intentional - generating code to remove these errors would
+ imply a false sense of security that the generator has done the right thing.
+
+ other return policies -- By default, Python never sees the return SCODE from
+ a COM function. The interface usually returns None if OK, else a COM exception
+ if "FAILED(scode)" is TRUE. You may need to change this if:
+ * EXCEPINFO is passed to the COM function. This is not detected and handled
+ * For some reason Python should always see the result SCODE, even if it
+ did fail or succeed. For example, some functions return a BOOLEAN result
+ in the SCODE, meaning Python should always see it.
+ * FAILED(scode) for the interface still has valid data to return (by default,
+ the code generated does not process the return values, and raise an exception
+ to Python/COM
+
+"""
+
+import re
+from . import makegwparse
+
+def make_framework_support(header_file_name, interface_name, bMakeInterface = 1, bMakeGateway = 1):
+ """Generate C++ code for a Python Interface and Gateway
+
+ header_file_name -- The full path to the .H file which defines the interface.
+ interface_name -- The name of the interface to search for, and to generate.
+ bMakeInterface = 1 -- Should interface (ie, client) support be generated.
+ bMakeGatewayInterface = 1 -- Should gateway (ie, server) support be generated.
+
+ This method will write a .cpp and .h file into the current directory,
+ (using the name of the interface to build the file name.
+
+ """
+ fin=open(header_file_name)
+ try:
+ interface = makegwparse.parse_interface_info(interface_name, fin)
+ finally:
+ fin.close()
+
+ if bMakeInterface and bMakeGateway:
+ desc = "Interface and Gateway"
+ elif bMakeInterface and not bMakeGateway:
+ desc = "Interface"
+ else:
+ desc = "Gateway"
+ if interface.name[:5]=="IEnum": # IEnum - use my really simple template-based one
+ import win32com.makegw.makegwenum
+ ifc_cpp_writer = win32com.makegw.makegwenum._write_enumifc_cpp
+ gw_cpp_writer = win32com.makegw.makegwenum._write_enumgw_cpp
+ else: # Use my harder working ones.
+ ifc_cpp_writer = _write_ifc_cpp
+ gw_cpp_writer = _write_gw_cpp
+
+ fout=open("Py%s.cpp" % interface.name, "w")
+ try:
+ fout.write(\
+'''\
+// This file implements the %s %s for Python.
+// Generated by makegw.py
+
+#include "shell_pch.h"
+''' % (interface.name, desc))
+# if bMakeGateway:
+# fout.write('#include "PythonCOMServer.h"\n')
+# if interface.base not in ["IUnknown", "IDispatch"]:
+# fout.write('#include "Py%s.h"\n' % interface.base)
+ fout.write('#include "Py%s.h"\n\n// @doc - This file contains autoduck documentation\n' % interface.name)
+ if bMakeInterface: ifc_cpp_writer(fout, interface)
+ if bMakeGateway: gw_cpp_writer(fout, interface)
+ finally:
+ fout.close()
+ fout=open("Py%s.h" % interface.name, "w")
+ try:
+ fout.write(\
+'''\
+// This file declares the %s %s for Python.
+// Generated by makegw.py
+''' % (interface.name, desc))
+
+ if bMakeInterface: _write_ifc_h(fout, interface)
+ if bMakeGateway: _write_gw_h(fout, interface)
+ finally:
+ fout.close()
+
+###########################################################################
+#
+# INTERNAL FUNCTIONS
+#
+#
+
+def _write_ifc_h(f, interface):
+ f.write(\
+'''\
+// ---------------------------------------------------
+//
+// Interface Declaration
+
+class Py%s : public Py%s
+{
+public:
+ MAKE_PYCOM_CTOR(Py%s);
+ static %s *GetI(PyObject *self);
+ static PyComTypeObject type;
+
+ // The Python methods
+''' % (interface.name, interface.base, interface.name, interface.name))
+ for method in interface.methods:
+ f.write('\tstatic PyObject *%s(PyObject *self, PyObject *args);\n' % method.name)
+ f.write(\
+'''\
+
+protected:
+ Py%s(IUnknown *pdisp);
+ ~Py%s();
+};
+''' % (interface.name, interface.name))
+
+def _write_ifc_cpp(f, interface):
+ name = interface.name
+ f.write(\
+'''\
+// ---------------------------------------------------
+//
+// Interface Implementation
+
+Py%(name)s::Py%(name)s(IUnknown *pdisp):
+ Py%(base)s(pdisp)
+{
+ ob_type = &type;
+}
+
+Py%(name)s::~Py%(name)s()
+{
+}
+
+/* static */ %(name)s *Py%(name)s::GetI(PyObject *self)
+{
+ return (%(name)s *)Py%(base)s::GetI(self);
+}
+
+''' % (interface.__dict__))
+
+ ptr = re.sub('[a-z]', '', interface.name)
+ strdict = {'interfacename':interface.name, 'ptr': ptr}
+ for method in interface.methods:
+ strdict['method'] = method.name
+ f.write(\
+'''\
+// @pymethod |Py%(interfacename)s|%(method)s|Description of %(method)s.
+PyObject *Py%(interfacename)s::%(method)s(PyObject *self, PyObject *args)
+{
+ %(interfacename)s *p%(ptr)s = GetI(self);
+ if ( p%(ptr)s == NULL )
+ return NULL;
+''' % strdict)
+ argsParseTuple = argsCOM = formatChars = codePost = \
+ codePobjects = codeCobjects = cleanup = cleanup_gil = ""
+ needConversion = 0
+# if method.name=="Stat": import win32dbg;win32dbg.brk()
+ for arg in method.args:
+ try:
+ argCvt = makegwparse.make_arg_converter(arg)
+ if arg.HasAttribute("in"):
+ val = argCvt.GetFormatChar()
+ if val:
+ f.write ('\t' + argCvt.GetAutoduckString() + "\n")
+ formatChars = formatChars + val
+ argsParseTuple = argsParseTuple + ", " + argCvt.GetParseTupleArg()
+ codePobjects = codePobjects + argCvt.DeclareParseArgTupleInputConverter()
+ codePost = codePost + argCvt.GetParsePostCode()
+ needConversion = needConversion or argCvt.NeedUSES_CONVERSION()
+ cleanup = cleanup + argCvt.GetInterfaceArgCleanup()
+ cleanup_gil = cleanup_gil + argCvt.GetInterfaceArgCleanupGIL()
+ comArgName, comArgDeclString = argCvt.GetInterfaceCppObjectInfo()
+ if comArgDeclString: # If we should declare a variable
+ codeCobjects = codeCobjects + "\t%s;\n" % (comArgDeclString)
+ argsCOM = argsCOM + ", " + comArgName
+ except makegwparse.error_not_supported as why:
+ f.write('// *** The input argument %s of type "%s" was not processed ***\n// Please check the conversion function is appropriate and exists!\n' % (arg.name, arg.raw_type))
+
+ f.write('\t%s %s;\n\tPyObject *ob%s;\n' % (arg.type, arg.name, arg.name))
+ f.write('\t// @pyparm |%s||Description for %s\n' % (arg.type, arg.name, arg.name))
+ codePost = codePost + '\tif (bPythonIsHappy && !PyObject_As%s( ob%s, &%s )) bPythonIsHappy = FALSE;\n' % (arg.type, arg.name, arg.name)
+
+ formatChars = formatChars + "O"
+ argsParseTuple = argsParseTuple + ", &ob%s" % (arg.name)
+
+ argsCOM = argsCOM + ", " + arg.name
+ cleanup = cleanup + "\tPyObject_Free%s(%s);\n" % (arg.type, arg.name)
+
+ if needConversion: f.write("\tUSES_CONVERSION;\n")
+ f.write(codePobjects);
+ f.write(codeCobjects);
+ f.write('\tif ( !PyArg_ParseTuple(args, "%s:%s"%s) )\n\t\treturn NULL;\n' % (formatChars, method.name, argsParseTuple))
+ if codePost:
+ f.write('\tBOOL bPythonIsHappy = TRUE;\n')
+ f.write(codePost);
+ f.write('\tif (!bPythonIsHappy) return NULL;\n')
+ strdict['argsCOM'] = argsCOM[1:]
+ strdict['cleanup'] = cleanup
+ strdict['cleanup_gil'] = cleanup_gil
+ f.write(\
+''' HRESULT hr;
+ PY_INTERFACE_PRECALL;
+ hr = p%(ptr)s->%(method)s(%(argsCOM)s );
+%(cleanup)s
+ PY_INTERFACE_POSTCALL;
+%(cleanup_gil)s
+ if ( FAILED(hr) )
+ return PyCom_BuildPyException(hr, p%(ptr)s, IID_%(interfacename)s );
+''' % strdict)
+ codePre = codePost = formatChars = codeVarsPass = codeDecl = ""
+ for arg in method.args:
+ if not arg.HasAttribute("out"):
+ continue
+ try:
+ argCvt = makegwparse.make_arg_converter(arg)
+ formatChar = argCvt.GetFormatChar()
+ if formatChar:
+ formatChars = formatChars + formatChar
+ codePre = codePre + argCvt.GetBuildForInterfacePreCode()
+ codePost = codePost + argCvt.GetBuildForInterfacePostCode()
+ codeVarsPass = codeVarsPass + ", " + argCvt.GetBuildValueArg()
+ codeDecl = codeDecl + argCvt.DeclareParseArgTupleInputConverter()
+ except makegwparse.error_not_supported as why:
+ f.write('// *** The output argument %s of type "%s" was not processed ***\n// %s\n' % (arg.name, arg.raw_type, why))
+ continue
+ if formatChars:
+ f.write('%s\n%s\tPyObject *pyretval = Py_BuildValue("%s"%s);\n%s\treturn pyretval;' % (codeDecl, codePre, formatChars, codeVarsPass, codePost))
+ else:
+ f.write('\tPy_INCREF(Py_None);\n\treturn Py_None;\n')
+ f.write('\n}\n\n')
+
+ f.write ('// @object Py%s|Description of the interface\n' % (name))
+ f.write('static struct PyMethodDef Py%s_methods[] =\n{\n' % name)
+ for method in interface.methods:
+ f.write('\t{ "%s", Py%s::%s, 1 }, // @pymeth %s|Description of %s\n' % (method.name, interface.name, method.name, method.name, method.name))
+
+ interfacebase = interface.base
+ f.write('''\
+ { NULL }
+};
+
+PyComTypeObject Py%(name)s::type("Py%(name)s",
+ &Py%(interfacebase)s::type,
+ sizeof(Py%(name)s),
+ Py%(name)s_methods,
+ GET_PYCOM_CTOR(Py%(name)s));
+''' % locals())
+
+def _write_gw_h(f, interface):
+ if interface.name[0] == "I":
+ gname = 'PyG' + interface.name[1:]
+ else:
+ gname = 'PyG' + interface.name
+ name = interface.name
+ if interface.base == "IUnknown" or interface.base == "IDispatch":
+ base_name = "PyGatewayBase"
+ else:
+ if interface.base[0] == "I":
+ base_name = 'PyG' + interface.base[1:]
+ else:
+ base_name = 'PyG' + interface.base
+ f.write(\
+'''\
+// ---------------------------------------------------
+//
+// Gateway Declaration
+
+class %s : public %s, public %s
+{
+protected:
+ %s(PyObject *instance) : %s(instance) { ; }
+ PYGATEWAY_MAKE_SUPPORT2(%s, %s, IID_%s, %s)
+
+''' % (gname, base_name, name, gname, base_name, gname, name, name, base_name))
+ if interface.base != "IUnknown":
+ f.write("\t// %s\n\t// *** Manually add %s method decls here\n\n" % (interface.base, interface.base))
+ else:
+ f.write('\n\n')
+
+ f.write("\t// %s\n" % name)
+
+ for method in interface.methods:
+ f.write('\tSTDMETHOD(%s)(\n' % method.name)
+ if method.args:
+ for arg in method.args[:-1]:
+ f.write("\t\t%s,\n" % (arg.GetRawDeclaration()))
+ arg = method.args[-1]
+ f.write("\t\t%s);\n\n" % (arg.GetRawDeclaration()))
+ else:
+ f.write('\t\tvoid);\n\n')
+
+ f.write('};\n')
+ f.close()
+
+def _write_gw_cpp(f, interface):
+ if interface.name[0] == "I":
+ gname = 'PyG' + interface.name[1:]
+ else:
+ gname = 'PyG' + interface.name
+ name = interface.name
+ if interface.base == "IUnknown" or interface.base == "IDispatch":
+ base_name = "PyGatewayBase"
+ else:
+ if interface.base[0] == "I":
+ base_name = 'PyG' + interface.base[1:]
+ else:
+ base_name = 'PyG' + interface.base
+ f.write('''\
+// ---------------------------------------------------
+//
+// Gateway Implementation
+''' % {'name':name, 'gname':gname, 'base_name':base_name})
+
+ for method in interface.methods:
+ f.write(\
+'''\
+STDMETHODIMP %s::%s(
+''' % (gname, method.name))
+
+ if method.args:
+ for arg in method.args[:-1]:
+ inoutstr = ']['.join(arg.inout)
+ f.write("\t\t/* [%s] */ %s,\n" % (inoutstr, arg.GetRawDeclaration()))
+
+ arg = method.args[-1]
+ inoutstr = ']['.join(arg.inout)
+ f.write("\t\t/* [%s] */ %s)\n" % (inoutstr, arg.GetRawDeclaration()))
+ else:
+ f.write('\t\tvoid)\n')
+
+ f.write("{\n\tPY_GATEWAY_METHOD;\n")
+ cout = 0
+ codePre = codePost = codeVars = ""
+ argStr = ""
+ needConversion = 0
+ formatChars = ""
+ if method.args:
+ for arg in method.args:
+ if arg.HasAttribute("out"):
+ cout = cout + 1
+ if arg.indirectionLevel ==2 :
+ f.write("\tif (%s==NULL) return E_POINTER;\n" % arg.name)
+ if arg.HasAttribute("in"):
+ try:
+ argCvt = makegwparse.make_arg_converter(arg)
+ argCvt.SetGatewayMode()
+ formatchar = argCvt.GetFormatChar();
+ needConversion = needConversion or argCvt.NeedUSES_CONVERSION()
+
+ if formatchar:
+ formatChars = formatChars + formatchar
+ codeVars = codeVars + argCvt.DeclareParseArgTupleInputConverter()
+ argStr = argStr + ", " + argCvt.GetBuildValueArg()
+ codePre = codePre + argCvt.GetBuildForGatewayPreCode()
+ codePost = codePost + argCvt.GetBuildForGatewayPostCode()
+ except makegwparse.error_not_supported as why:
+ f.write('// *** The input argument %s of type "%s" was not processed ***\n// - Please ensure this conversion function exists, and is appropriate\n// - %s\n' % (arg.name, arg.raw_type, why))
+ f.write('\tPyObject *ob%s = PyObject_From%s(%s);\n' % (arg.name, arg.type, arg.name))
+ f.write('\tif (ob%s==NULL) return MAKE_PYCOM_GATEWAY_FAILURE_CODE("%s");\n' % (arg.name, method.name))
+ codePost = codePost + "\tPy_DECREF(ob%s);\n" % arg.name
+ formatChars = formatChars + "O"
+ argStr = argStr + ", ob%s" % (arg.name)
+
+ if needConversion: f.write('\tUSES_CONVERSION;\n')
+ f.write(codeVars)
+ f.write(codePre)
+ if cout:
+ f.write("\tPyObject *result;\n")
+ resStr = "&result"
+ else:
+ resStr = "NULL"
+
+ if formatChars:
+ fullArgStr = '%s, "%s"%s' % (resStr, formatChars, argStr)
+ else:
+ fullArgStr = resStr
+
+ f.write('\tHRESULT hr=InvokeViaPolicy("%s", %s);\n' % (method.name, fullArgStr))
+ f.write(codePost)
+ if cout:
+ f.write("\tif (FAILED(hr)) return hr;\n")
+ f.write("\t// Process the Python results, and convert back to the real params\n")
+ # process the output arguments.
+ formatChars = codePobjects = codePost = argsParseTuple = ""
+ needConversion = 0
+ for arg in method.args:
+ if not arg.HasAttribute("out"):
+ continue
+ try:
+ argCvt = makegwparse.make_arg_converter(arg)
+ argCvt.SetGatewayMode()
+ val = argCvt.GetFormatChar()
+ if val:
+ formatChars = formatChars + val
+ argsParseTuple = argsParseTuple + ", " + argCvt.GetParseTupleArg()
+ codePobjects = codePobjects + argCvt.DeclareParseArgTupleInputConverter()
+ codePost = codePost + argCvt.GetParsePostCode()
+ needConversion = needConversion or argCvt.NeedUSES_CONVERSION()
+ except makegwparse.error_not_supported as why:
+ f.write('// *** The output argument %s of type "%s" was not processed ***\n// %s\n' % (arg.name, arg.raw_type, why))
+
+ if formatChars: # If I have any to actually process.
+ if len(formatChars)==1:
+ parseFn = "PyArg_Parse"
+ else:
+ parseFn = "PyArg_ParseTuple"
+ if codePobjects: f.write(codePobjects)
+ f.write('\tif (!%s(result, "%s" %s))\n\t\treturn MAKE_PYCOM_GATEWAY_FAILURE_CODE("%s");\n' % (parseFn, formatChars, argsParseTuple, method.name))
+ if codePost:
+ f.write('\tBOOL bPythonIsHappy = TRUE;\n')
+ f.write(codePost)
+ f.write('\tif (!bPythonIsHappy) hr = MAKE_PYCOM_GATEWAY_FAILURE_CODE("%s");\n' % method.name)
+ f.write('\tPy_DECREF(result);\n');
+ f.write('\treturn hr;\n}\n\n')
+
+def test():
+# make_framework_support("d:\\msdev\\include\\objidl.h", "ILockBytes")
+ make_framework_support("d:\\msdev\\include\\objidl.h", "IStorage")
+# make_framework_support("d:\\msdev\\include\\objidl.h", "IEnumSTATSTG")
diff --git a/Lib/site-packages/win32com/makegw/makegwenum.py b/Lib/site-packages/win32com/makegw/makegwenum.py
new file mode 100644
index 0000000..fcf67e0
--- /dev/null
+++ b/Lib/site-packages/win32com/makegw/makegwenum.py
@@ -0,0 +1,317 @@
+"""Utility file for generating PyIEnum support.
+
+This is almost a 'template' file. It simplay contains almost full
+C++ source code for PyIEnum* support, and the Python code simply
+substitutes the appropriate interface name.
+
+This module is notmally not used directly - the @makegw@ module
+automatically calls this.
+"""
+#
+# INTERNAL FUNCTIONS
+#
+#
+import string
+
+def is_interface_enum(enumtype):
+ return not (enumtype[0] in string.uppercase and enumtype[2] in string.uppercase)
+
+
+def _write_enumifc_cpp(f, interface):
+ enumtype = interface.name[5:]
+ if is_interface_enum(enumtype):
+ # Assume an interface.
+ enum_interface = "I" + enumtype[:-1]
+ converter = "PyObject *ob = PyCom_PyObjectFromIUnknown(rgVar[i], IID_%(enum_interface)s, FALSE);" % locals()
+ arraydeclare = "%(enum_interface)s **rgVar = new %(enum_interface)s *[celt];" % locals()
+ else:
+ # Enum of a simple structure
+ converter = "PyObject *ob = PyCom_PyObjectFrom%(enumtype)s(&rgVar[i]);" % locals()
+ arraydeclare = "%(enumtype)s *rgVar = new %(enumtype)s[celt];" % locals()
+
+ f.write(\
+'''
+// ---------------------------------------------------
+//
+// Interface Implementation
+
+PyIEnum%(enumtype)s::PyIEnum%(enumtype)s(IUnknown *pdisp):
+ PyIUnknown(pdisp)
+{
+ ob_type = &type;
+}
+
+PyIEnum%(enumtype)s::~PyIEnum%(enumtype)s()
+{
+}
+
+/* static */ IEnum%(enumtype)s *PyIEnum%(enumtype)s::GetI(PyObject *self)
+{
+ return (IEnum%(enumtype)s *)PyIUnknown::GetI(self);
+}
+
+// @pymethod object|PyIEnum%(enumtype)s|Next|Retrieves a specified number of items in the enumeration sequence.
+PyObject *PyIEnum%(enumtype)s::Next(PyObject *self, PyObject *args)
+{
+ long celt = 1;
+ // @pyparm int|num|1|Number of items to retrieve.
+ if ( !PyArg_ParseTuple(args, "|l:Next", &celt) )
+ return NULL;
+
+ IEnum%(enumtype)s *pIE%(enumtype)s = GetI(self);
+ if ( pIE%(enumtype)s == NULL )
+ return NULL;
+
+ %(arraydeclare)s
+ if ( rgVar == NULL ) {
+ PyErr_SetString(PyExc_MemoryError, "allocating result %(enumtype)ss");
+ return NULL;
+ }
+
+ int i;
+/* for ( i = celt; i--; )
+ // *** possibly init each structure element???
+*/
+
+ ULONG celtFetched = 0;
+ PY_INTERFACE_PRECALL;
+ HRESULT hr = pIE%(enumtype)s->Next(celt, rgVar, &celtFetched);
+ PY_INTERFACE_POSTCALL;
+ if ( HRESULT_CODE(hr) != ERROR_NO_MORE_ITEMS && FAILED(hr) )
+ {
+ delete [] rgVar;
+ return PyCom_BuildPyException(hr,pIE%(enumtype)s, IID_IE%(enumtype)s);
+ }
+
+ PyObject *result = PyTuple_New(celtFetched);
+ if ( result != NULL )
+ {
+ for ( i = celtFetched; i--; )
+ {
+ %(converter)s
+ if ( ob == NULL )
+ {
+ Py_DECREF(result);
+ result = NULL;
+ break;
+ }
+ PyTuple_SET_ITEM(result, i, ob);
+ }
+ }
+
+/* for ( i = celtFetched; i--; )
+ // *** possibly cleanup each structure element???
+*/
+ delete [] rgVar;
+ return result;
+}
+
+// @pymethod |PyIEnum%(enumtype)s|Skip|Skips over the next specified elementes.
+PyObject *PyIEnum%(enumtype)s::Skip(PyObject *self, PyObject *args)
+{
+ long celt;
+ if ( !PyArg_ParseTuple(args, "l:Skip", &celt) )
+ return NULL;
+
+ IEnum%(enumtype)s *pIE%(enumtype)s = GetI(self);
+ if ( pIE%(enumtype)s == NULL )
+ return NULL;
+
+ PY_INTERFACE_PRECALL;
+ HRESULT hr = pIE%(enumtype)s->Skip(celt);
+ PY_INTERFACE_POSTCALL;
+ if ( FAILED(hr) )
+ return PyCom_BuildPyException(hr, pIE%(enumtype)s, IID_IE%(enumtype)s);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+// @pymethod |PyIEnum%(enumtype)s|Reset|Resets the enumeration sequence to the beginning.
+PyObject *PyIEnum%(enumtype)s::Reset(PyObject *self, PyObject *args)
+{
+ if ( !PyArg_ParseTuple(args, ":Reset") )
+ return NULL;
+
+ IEnum%(enumtype)s *pIE%(enumtype)s = GetI(self);
+ if ( pIE%(enumtype)s == NULL )
+ return NULL;
+
+ PY_INTERFACE_PRECALL;
+ HRESULT hr = pIE%(enumtype)s->Reset();
+ PY_INTERFACE_POSTCALL;
+ if ( FAILED(hr) )
+ return PyCom_BuildPyException(hr, pIE%(enumtype)s, IID_IE%(enumtype)s);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+// @pymethod |PyIEnum%(enumtype)s|Clone|Creates another enumerator that contains the same enumeration state as the current one
+PyObject *PyIEnum%(enumtype)s::Clone(PyObject *self, PyObject *args)
+{
+ if ( !PyArg_ParseTuple(args, ":Clone") )
+ return NULL;
+
+ IEnum%(enumtype)s *pIE%(enumtype)s = GetI(self);
+ if ( pIE%(enumtype)s == NULL )
+ return NULL;
+
+ IEnum%(enumtype)s *pClone;
+ PY_INTERFACE_PRECALL;
+ HRESULT hr = pIE%(enumtype)s->Clone(&pClone);
+ PY_INTERFACE_POSTCALL;
+ if ( FAILED(hr) )
+ return PyCom_BuildPyException(hr, pIE%(enumtype)s, IID_IE%(enumtype)s);
+
+ return PyCom_PyObjectFromIUnknown(pClone, IID_IEnum%(enumtype)s, FALSE);
+}
+
+// @object PyIEnum%(enumtype)s|A Python interface to IEnum%(enumtype)s
+static struct PyMethodDef PyIEnum%(enumtype)s_methods[] =
+{
+ { "Next", PyIEnum%(enumtype)s::Next, 1 }, // @pymeth Next|Retrieves a specified number of items in the enumeration sequence.
+ { "Skip", PyIEnum%(enumtype)s::Skip, 1 }, // @pymeth Skip|Skips over the next specified elementes.
+ { "Reset", PyIEnum%(enumtype)s::Reset, 1 }, // @pymeth Reset|Resets the enumeration sequence to the beginning.
+ { "Clone", PyIEnum%(enumtype)s::Clone, 1 }, // @pymeth Clone|Creates another enumerator that contains the same enumeration state as the current one.
+ { NULL }
+};
+
+PyComEnumTypeObject PyIEnum%(enumtype)s::type("PyIEnum%(enumtype)s",
+ &PyIUnknown::type,
+ sizeof(PyIEnum%(enumtype)s),
+ PyIEnum%(enumtype)s_methods,
+ GET_PYCOM_CTOR(PyIEnum%(enumtype)s));
+''' % locals() )
+
+
+
+def _write_enumgw_cpp(f, interface):
+ enumtype = interface.name[5:]
+ if is_interface_enum(enumtype):
+ # Assume an interface.
+ enum_interface = "I" + enumtype[:-1]
+ converter = "if ( !PyCom_InterfaceFromPyObject(ob, IID_%(enum_interface)s, (void **)&rgVar[i], FALSE) )" % locals()
+ argdeclare="%(enum_interface)s __RPC_FAR * __RPC_FAR *rgVar" % locals()
+ else:
+ argdeclare="%(enumtype)s __RPC_FAR *rgVar" % locals()
+ converter="if ( !PyCom_PyObjectAs%(enumtype)s(ob, &rgVar[i]) )" % locals()
+ f.write(
+'''
+// ---------------------------------------------------
+//
+// Gateway Implementation
+
+// Std delegation
+STDMETHODIMP_(ULONG) PyGEnum%(enumtype)s::AddRef(void) {return PyGatewayBase::AddRef();}
+STDMETHODIMP_(ULONG) PyGEnum%(enumtype)s::Release(void) {return PyGatewayBase::Release();}
+STDMETHODIMP PyGEnum%(enumtype)s::QueryInterface(REFIID iid, void ** obj) {return PyGatewayBase::QueryInterface(iid, obj);}
+STDMETHODIMP PyGEnum%(enumtype)s::GetTypeInfoCount(UINT FAR* pctInfo) {return PyGatewayBase::GetTypeInfoCount(pctInfo);}
+STDMETHODIMP PyGEnum%(enumtype)s::GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo FAR* FAR* pptInfo) {return PyGatewayBase::GetTypeInfo(itinfo, lcid, pptInfo);}
+STDMETHODIMP PyGEnum%(enumtype)s::GetIDsOfNames(REFIID refiid, OLECHAR FAR* FAR* rgszNames, UINT cNames, LCID lcid, DISPID FAR* rgdispid) {return PyGatewayBase::GetIDsOfNames( refiid, rgszNames, cNames, lcid, rgdispid);}
+STDMETHODIMP PyGEnum%(enumtype)s::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR* params, VARIANT FAR* pVarResult, EXCEPINFO FAR* pexcepinfo, UINT FAR* puArgErr) {return PyGatewayBase::Invoke( dispid, riid, lcid, wFlags, params, pVarResult, pexcepinfo, puArgErr);}
+
+STDMETHODIMP PyGEnum%(enumtype)s::Next(
+ /* [in] */ ULONG celt,
+ /* [length_is][size_is][out] */ %(argdeclare)s,
+ /* [out] */ ULONG __RPC_FAR *pCeltFetched)
+{
+ PY_GATEWAY_METHOD;
+ PyObject *result;
+ HRESULT hr = InvokeViaPolicy("Next", &result, "i", celt);
+ if ( FAILED(hr) )
+ return hr;
+
+ if ( !PySequence_Check(result) )
+ goto error;
+ int len;
+ len = PyObject_Length(result);
+ if ( len == -1 )
+ goto error;
+ if ( len > (int)celt)
+ len = celt;
+
+ if ( pCeltFetched )
+ *pCeltFetched = len;
+
+ int i;
+ for ( i = 0; i < len; ++i )
+ {
+ PyObject *ob = PySequence_GetItem(result, i);
+ if ( ob == NULL )
+ goto error;
+
+ %(converter)s
+ {
+ Py_DECREF(result);
+ return PyCom_SetCOMErrorFromPyException(IID_IEnum%(enumtype)s);
+ }
+ }
+
+ Py_DECREF(result);
+
+ return len < (int)celt ? S_FALSE : S_OK;
+
+ error:
+ PyErr_Clear(); // just in case
+ Py_DECREF(result);
+ return PyCom_SetCOMErrorFromSimple(E_FAIL, IID_IEnum%(enumtype)s, "Next() did not return a sequence of objects");
+}
+
+STDMETHODIMP PyGEnum%(enumtype)s::Skip(
+ /* [in] */ ULONG celt)
+{
+ PY_GATEWAY_METHOD;
+ return InvokeViaPolicy("Skip", NULL, "i", celt);
+}
+
+STDMETHODIMP PyGEnum%(enumtype)s::Reset(void)
+{
+ PY_GATEWAY_METHOD;
+ return InvokeViaPolicy("Reset");
+}
+
+STDMETHODIMP PyGEnum%(enumtype)s::Clone(
+ /* [out] */ IEnum%(enumtype)s __RPC_FAR *__RPC_FAR *ppEnum)
+{
+ PY_GATEWAY_METHOD;
+ PyObject * result;
+ HRESULT hr = InvokeViaPolicy("Clone", &result);
+ if ( FAILED(hr) )
+ return hr;
+
+ /*
+ ** Make sure we have the right kind of object: we should have some kind
+ ** of IUnknown subclass wrapped into a PyIUnknown instance.
+ */
+ if ( !PyIBase::is_object(result, &PyIUnknown::type) )
+ {
+ /* the wrong kind of object was returned to us */
+ Py_DECREF(result);
+ return PyCom_SetCOMErrorFromSimple(E_FAIL, IID_IEnum%(enumtype)s);
+ }
+
+ /*
+ ** Get the IUnknown out of the thing. note that the Python ob maintains
+ ** a reference, so we don't have to explicitly AddRef() here.
+ */
+ IUnknown *punk = ((PyIUnknown *)result)->m_obj;
+ if ( !punk )
+ {
+ /* damn. the object was released. */
+ Py_DECREF(result);
+ return PyCom_SetCOMErrorFromSimple(E_FAIL, IID_IEnum%(enumtype)s);
+ }
+
+ /*
+ ** Get the interface we want. note it is returned with a refcount.
+ ** This QI is actually going to instantiate a PyGEnum%(enumtype)s.
+ */
+ hr = punk->QueryInterface(IID_IEnum%(enumtype)s, (LPVOID *)ppEnum);
+
+ /* done with the result; this DECREF is also for */
+ Py_DECREF(result);
+
+ return PyCom_SetCOMErrorFromSimple(hr, IID_IEnum%(enumtype)s, "Python could not convert the result from Next() into the required COM interface");
+}
+''' % locals())
diff --git a/Lib/site-packages/win32com/makegw/makegwparse.py b/Lib/site-packages/win32com/makegw/makegwparse.py
new file mode 100644
index 0000000..8399098
--- /dev/null
+++ b/Lib/site-packages/win32com/makegw/makegwparse.py
@@ -0,0 +1,783 @@
+"""Utilities for makegw - Parse a header file to build an interface
+
+ This module contains the core code for parsing a header file describing a
+ COM interface, and building it into an "Interface" structure.
+
+ Each Interface has methods, and each method has arguments.
+
+ Each argument knows how to use Py_BuildValue or Py_ParseTuple to
+ exchange itself with Python.
+
+ See the @win32com.makegw@ module for information in building a COM
+ interface
+"""
+import re
+import traceback
+
+class error_not_found(Exception):
+ def __init__(self, msg="The requested item could not be found"):
+ super(error_not_found, self).__init__(msg)
+
+class error_not_supported(Exception):
+ def __init__(self, msg="The required functionality is not supported"):
+ super(error_not_supported, self).__init__(msg)
+
+VERBOSE=0
+DEBUG=0
+
+## NOTE : For interfaces as params to work correctly, you must
+## make sure any PythonCOM extensions which expose the interface are loaded
+## before generating.
+
+
+class ArgFormatter:
+ """An instance for a specific type of argument. Knows how to convert itself"""
+ def __init__(self, arg, builtinIndirection, declaredIndirection = 0):
+ #print 'init:', arg.name, builtinIndirection, declaredIndirection, arg.indirectionLevel
+ self.arg = arg
+ self.builtinIndirection = builtinIndirection
+ self.declaredIndirection = declaredIndirection
+ self.gatewayMode = 0
+ def _IndirectPrefix(self, indirectionFrom, indirectionTo):
+ """Given the indirection level I was declared at (0=Normal, 1=*, 2=**)
+ return a string prefix so I can pass to a function with the
+ required indirection (where the default is the indirection of the method's param.
+
+ eg, assuming my arg has indirection level of 2, if this function was passed 1
+ it would return "&", so that a variable declared with indirection of 1
+ can be prefixed with this to turn it into the indirection level required of 2
+ """
+ dif = indirectionFrom - indirectionTo
+ if dif==0:
+ return ""
+ elif dif==-1:
+ return "&"
+ elif dif==1:
+ return "*"
+ else:
+ return "?? (%d)" % (dif,)
+ raise error_not_supported("Can't indirect this far - please fix me :-)")
+ def GetIndirectedArgName(self, indirectFrom, indirectionTo):
+ #print 'get:',self.arg.name, indirectFrom,self._GetDeclaredIndirection() + self.builtinIndirection, indirectionTo, self.arg.indirectionLevel
+
+ if indirectFrom is None:
+ ### ACK! this does not account for [in][out] variables.
+ ### when this method is called, we need to know which
+ indirectFrom = self._GetDeclaredIndirection() + self.builtinIndirection
+
+ return self._IndirectPrefix(indirectFrom, indirectionTo) + self.arg.name
+ def GetBuildValueArg(self):
+ "Get the argument to be passes to Py_BuildValue"
+ return self.arg.name
+ def GetParseTupleArg(self):
+ "Get the argument to be passed to PyArg_ParseTuple"
+ if self.gatewayMode:
+ # use whatever they were declared with
+ return self.GetIndirectedArgName(None, 1)
+ # local declarations have just their builtin indirection
+ return self.GetIndirectedArgName(self.builtinIndirection, 1)
+ def GetInterfaceCppObjectInfo(self):
+ """Provide information about the C++ object used.
+
+ Simple variables (such as integers) can declare their type (eg an integer)
+ and use it as the target of both PyArg_ParseTuple and the COM function itself.
+
+ More complex types require a PyObject * declared as the target of PyArg_ParseTuple,
+ then some conversion routine to the C++ object which is actually passed to COM.
+
+ This method provides the name, and optionally the type of that C++ variable.
+ If the type if provided, the caller will likely generate a variable declaration.
+ The name must always be returned.
+
+ Result is a tuple of (variableName, [DeclareType|None|""])
+ """
+
+ # the first return element is the variable to be passed as
+ # an argument to an interface method. the variable was
+ # declared with only its builtin indirection level. when
+ # we pass it, we'll need to pass in whatever amount of
+ # indirection was applied (plus the builtin amount)
+ # the second return element is the variable declaration; it
+ # should simply be builtin indirection
+ return self.GetIndirectedArgName(self.builtinIndirection, self.arg.indirectionLevel + self.builtinIndirection), \
+ "%s %s" % (self.GetUnconstType(), self.arg.name)
+
+ def GetInterfaceArgCleanup(self):
+ "Return cleanup code for C++ args passed to the interface method."
+ if DEBUG:
+ return "/* GetInterfaceArgCleanup output goes here: %s */\n" % self.arg.name
+ else:
+ return ""
+
+ def GetInterfaceArgCleanupGIL(self):
+ """Return cleanup code for C++ args passed to the interface
+ method that must be executed with the GIL held"""
+ if DEBUG:
+ return "/* GetInterfaceArgCleanup (GIL held) output goes here: %s */\n" % self.arg.name
+ else:
+ return ""
+
+ def GetUnconstType(self):
+ return self.arg.unc_type
+
+ def SetGatewayMode(self):
+ self.gatewayMode = 1
+ def _GetDeclaredIndirection(self):
+ return self.arg.indirectionLevel
+ print('declared:', self.arg.name, self.gatewayMode)
+ if self.gatewayMode:
+ return self.arg.indirectionLevel
+ else:
+ return self.declaredIndirection
+ def DeclareParseArgTupleInputConverter(self):
+ "Declare the variable used as the PyArg_ParseTuple param for a gateway"
+ # Only declare it??
+ #if self.arg.indirectionLevel==0:
+ # return "\t%s %s;\n" % (self.arg.type, self.arg.name)
+ #else:
+ if DEBUG:
+ return "/* Declare ParseArgTupleInputConverter goes here: %s */\n" % self.arg.name
+ else:
+ return ""
+ def GetParsePostCode(self):
+ "Get a string of C++ code to be executed after (ie, to finalise) the PyArg_ParseTuple conversion"
+ if DEBUG:
+ return "/* GetParsePostCode code goes here: %s */\n" % self.arg.name
+ else:
+ return ""
+ def GetBuildForInterfacePreCode(self):
+ "Get a string of C++ code to be executed before (ie, to initialise) the Py_BuildValue conversion for Interfaces"
+ if DEBUG:
+ return "/* GetBuildForInterfacePreCode goes here: %s */\n" % self.arg.name
+ else:
+ return ""
+ def GetBuildForGatewayPreCode(self):
+ "Get a string of C++ code to be executed before (ie, to initialise) the Py_BuildValue conversion for Gateways"
+ s = self.GetBuildForInterfacePreCode() # Usually the same
+ if DEBUG:
+ if s[:4] == "/* G":
+ s = "/* GetBuildForGatewayPreCode goes here: %s */\n" % self.arg.name
+ return s
+ def GetBuildForInterfacePostCode(self):
+ "Get a string of C++ code to be executed after (ie, to finalise) the Py_BuildValue conversion for Interfaces"
+ if DEBUG:
+ return "/* GetBuildForInterfacePostCode goes here: %s */\n" % self.arg.name
+ return ""
+ def GetBuildForGatewayPostCode(self):
+ "Get a string of C++ code to be executed after (ie, to finalise) the Py_BuildValue conversion for Gateways"
+ s = self.GetBuildForInterfacePostCode() # Usually the same
+ if DEBUG:
+ if s[:4] == "/* G":
+ s = "/* GetBuildForGatewayPostCode goes here: %s */\n" % self.arg.name
+ return s
+ def GetAutoduckString(self):
+ return '// @pyparm %s|%s||Description for %s' % (self._GetPythonTypeDesc(), self.arg.name, self.arg.name)
+ def _GetPythonTypeDesc(self):
+ "Returns a string with the description of the type. Used for doco purposes"
+ return None
+ def NeedUSES_CONVERSION(self):
+ "Determines if this arg forces a USES_CONVERSION macro"
+ return 0
+
+# Special formatter for floats since they're smaller than Python floats.
+class ArgFormatterFloat(ArgFormatter):
+ def GetFormatChar(self):
+ return "f"
+ def DeclareParseArgTupleInputConverter(self):
+ # Declare a double variable
+ return "\tdouble dbl%s;\n" % self.arg.name
+ def GetParseTupleArg(self):
+ return "&dbl" + self.arg.name
+ def _GetPythonTypeDesc(self):
+ return "float"
+ def GetBuildValueArg(self):
+ return "&dbl" + self.arg.name
+ def GetBuildForInterfacePreCode(self):
+ return "\tdbl" + self.arg.name + " = " + self.arg.name + ";\n"
+ def GetBuildForGatewayPreCode(self):
+ return "\tdbl%s = " % self.arg.name + self._IndirectPrefix( \
+ self._GetDeclaredIndirection(),
+ 0) + self.arg.name + ";\n"
+ def GetParsePostCode(self):
+ s = "\t"
+ if self.gatewayMode:
+ s = s + self._IndirectPrefix(
+ self._GetDeclaredIndirection(),
+ 0)
+ s = s + self.arg.name
+ s = s + " = (float)dbl%s;\n" % self.arg.name
+ return s
+
+# Special formatter for Shorts because they're
+# a different size than Python ints!
+class ArgFormatterShort(ArgFormatter):
+ def GetFormatChar(self):
+ return "i"
+ def DeclareParseArgTupleInputConverter(self):
+ # Declare a double variable
+ return "\tINT i%s;\n" % self.arg.name
+ def GetParseTupleArg(self):
+ return "&i" + self.arg.name
+ def _GetPythonTypeDesc(self):
+ return "int"
+ def GetBuildValueArg(self):
+ return "&i" + self.arg.name
+ def GetBuildForInterfacePreCode(self):
+ return "\ti" + self.arg.name + " = " + self.arg.name + ";\n"
+ def GetBuildForGatewayPreCode(self):
+ return "\ti%s = " % self.arg.name + self._IndirectPrefix( \
+ self._GetDeclaredIndirection(),
+ 0) + self.arg.name + ";\n"
+ def GetParsePostCode(self):
+ s = "\t"
+ if self.gatewayMode:
+ s = s + self._IndirectPrefix(
+ self._GetDeclaredIndirection(),
+ 0)
+ s = s + self.arg.name
+ s = s + " = i%s;\n" % self.arg.name
+ return s
+
+# for types which are 64bits on AMD64 - eg, HWND
+class ArgFormatterLONG_PTR(ArgFormatter):
+ def GetFormatChar(self):
+ return "O"
+ def DeclareParseArgTupleInputConverter(self):
+ # Declare a PyObject variable
+ return "\tPyObject *ob%s;\n" % self.arg.name
+ def GetParseTupleArg(self):
+ return "&ob"+self.arg.name
+ def _GetPythonTypeDesc(self):
+ return "int/long"
+ def GetBuildValueArg(self):
+ return "ob" + self.arg.name
+ def GetBuildForInterfacePostCode(self):
+ return "\tPy_XDECREF(ob%s);\n" % self.arg.name
+ def DeclareParseArgTupleInputConverter(self):
+ # Declare a PyObject variable
+ return "\tPyObject *ob%s;\n" % self.arg.name
+
+ def GetParsePostCode(self):
+ return "\tif (bPythonIsHappy && !PyWinLong_AsULONG_PTR(ob%s, (ULONG_PTR *)%s)) bPythonIsHappy = FALSE;\n" % (self.arg.name, self.GetIndirectedArgName(None, 2))
+ def GetBuildForInterfacePreCode(self):
+ notdirected = self.GetIndirectedArgName(None, 1)
+ return "\tob%s = PyWinObject_FromULONG_PTR(%s);\n" % \
+ (self.arg.name, notdirected)
+ def GetBuildForGatewayPostCode(self):
+ return "\tPy_XDECREF(ob%s);\n" % self.arg.name
+
+class ArgFormatterPythonCOM(ArgFormatter):
+ """An arg formatter for types exposed in the PythonCOM module"""
+ def GetFormatChar(self):
+ return "O"
+ #def GetInterfaceCppObjectInfo(self):
+ # return ArgFormatter.GetInterfaceCppObjectInfo(self)[0], \
+ # "%s %s%s" % (self.arg.unc_type, "*" * self._GetDeclaredIndirection(), self.arg.name)
+ def DeclareParseArgTupleInputConverter(self):
+ # Declare a PyObject variable
+ return "\tPyObject *ob%s;\n" % self.arg.name
+ def GetParseTupleArg(self):
+ return "&ob"+self.arg.name
+ def _GetPythonTypeDesc(self):
+ return "" % self.arg.type
+ def GetBuildValueArg(self):
+ return "ob" + self.arg.name
+ def GetBuildForInterfacePostCode(self):
+ return "\tPy_XDECREF(ob%s);\n" % self.arg.name
+ def DeclareParseArgTupleInputConverter(self):
+ # Declare a PyObject variable
+ return "\tPyObject *ob%s;\n" % self.arg.name
+
+class ArgFormatterBSTR(ArgFormatterPythonCOM):
+ def _GetPythonTypeDesc(self):
+ return ""
+ def GetParsePostCode(self):
+ return "\tif (bPythonIsHappy && !PyWinObject_AsBstr(ob%s, %s)) bPythonIsHappy = FALSE;\n" % (self.arg.name, self.GetIndirectedArgName(None, 2))
+ def GetBuildForInterfacePreCode(self):
+ notdirected = self.GetIndirectedArgName(None, 1)
+ return "\tob%s = MakeBstrToObj(%s);\n" % \
+ (self.arg.name, notdirected)
+ def GetBuildForInterfacePostCode(self):
+ return "\tSysFreeString(%s);\n" % (self.arg.name,) + \
+ ArgFormatterPythonCOM.GetBuildForInterfacePostCode(self)
+ def GetBuildForGatewayPostCode(self):
+ return "\tPy_XDECREF(ob%s);\n" % self.arg.name
+
+class ArgFormatterOLECHAR(ArgFormatterPythonCOM):
+ def _GetPythonTypeDesc(self):
+ return ""
+ def GetUnconstType(self):
+ if self.arg.type[:3]=="LPC":
+ return self.arg.type[:2] + self.arg.type[3:]
+ else:
+ return self.arg.unc_type
+ def GetParsePostCode(self):
+ return "\tif (bPythonIsHappy && !PyWinObject_AsBstr(ob%s, %s)) bPythonIsHappy = FALSE;\n" % (self.arg.name, self.GetIndirectedArgName(None, 2))
+ def GetInterfaceArgCleanup(self):
+ return "\tSysFreeString(%s);\n" % self.GetIndirectedArgName(None, 1)
+ def GetBuildForInterfacePreCode(self):
+ # the variable was declared with just its builtin indirection
+ notdirected = self.GetIndirectedArgName(self.builtinIndirection, 1)
+ return "\tob%s = MakeOLECHARToObj(%s);\n" % \
+ (self.arg.name, notdirected)
+ def GetBuildForInterfacePostCode(self):
+ # memory returned into an OLECHAR should be freed
+ return "\tCoTaskMemFree(%s);\n" % (self.arg.name,) + \
+ ArgFormatterPythonCOM.GetBuildForInterfacePostCode(self)
+ def GetBuildForGatewayPostCode(self):
+ return "\tPy_XDECREF(ob%s);\n" % self.arg.name
+
+class ArgFormatterTCHAR(ArgFormatterPythonCOM):
+ def _GetPythonTypeDesc(self):
+ return "string/"
+ def GetUnconstType(self):
+ if self.arg.type[:3]=="LPC":
+ return self.arg.type[:2] + self.arg.type[3:]
+ else:
+ return self.arg.unc_type
+ def GetParsePostCode(self):
+ return "\tif (bPythonIsHappy && !PyWinObject_AsTCHAR(ob%s, %s)) bPythonIsHappy = FALSE;\n" % (self.arg.name, self.GetIndirectedArgName(None, 2))
+ def GetInterfaceArgCleanup(self):
+ return "\tPyWinObject_FreeTCHAR(%s);\n" % self.GetIndirectedArgName(None, 1)
+ def GetBuildForInterfacePreCode(self):
+ # the variable was declared with just its builtin indirection
+ notdirected = self.GetIndirectedArgName(self.builtinIndirection, 1)
+ return "\tob%s = PyWinObject_FromTCHAR(%s);\n" % \
+ (self.arg.name, notdirected)
+ def GetBuildForInterfacePostCode(self):
+ return "// ??? - TCHAR post code\n"
+ def GetBuildForGatewayPostCode(self):
+ return "\tPy_XDECREF(ob%s);\n" % self.arg.name
+
+class ArgFormatterIID(ArgFormatterPythonCOM):
+ def _GetPythonTypeDesc(self):
+ return ""
+ def GetParsePostCode(self):
+ return "\tif (!PyWinObject_AsIID(ob%s, &%s)) bPythonIsHappy = FALSE;\n" % (self.arg.name, self.arg.name)
+ def GetBuildForInterfacePreCode(self):
+# notdirected = self.GetIndirectedArgName(self.arg.indirectionLevel, 0)
+ notdirected = self.GetIndirectedArgName(None, 0)
+ return "\tob%s = PyWinObject_FromIID(%s);\n" % (self.arg.name, notdirected)
+ def GetInterfaceCppObjectInfo(self):
+ return self.arg.name, "IID %s" % (self.arg.name)
+
+class ArgFormatterTime(ArgFormatterPythonCOM):
+ def __init__(self, arg, builtinIndirection, declaredIndirection = 0):
+ # we don't want to declare LPSYSTEMTIME / LPFILETIME objects
+ if arg.indirectionLevel == 0 and arg.unc_type[:2] == "LP":
+ arg.unc_type = arg.unc_type[2:]
+ # reduce the builtin and increment the declaration
+ arg.indirectionLevel = arg.indirectionLevel + 1
+ builtinIndirection = 0
+ ArgFormatterPythonCOM.__init__(self, arg, builtinIndirection, declaredIndirection)
+
+ def _GetPythonTypeDesc(self):
+ return ""
+ def GetParsePostCode(self):
+ # variable was declared with only the builtinIndirection
+ ### NOTE: this is an [in] ... so use only builtin
+ return '\tif (!PyTime_Check(ob%s)) {\n\t\tPyErr_SetString(PyExc_TypeError, "The argument must be a PyTime object");\n\t\tbPythonIsHappy = FALSE;\n\t}\n\tif (!((PyTime *)ob%s)->GetTime(%s)) bPythonIsHappy = FALSE;\n' % (self.arg.name, self.arg.name, self.GetIndirectedArgName(self.builtinIndirection, 1))
+ def GetBuildForInterfacePreCode(self):
+ ### use just the builtinIndirection again...
+ notdirected = self.GetIndirectedArgName(self.builtinIndirection,0)
+ return "\tob%s = new PyTime(%s);\n" % (self.arg.name, notdirected)
+ def GetBuildForInterfacePostCode(self):
+ ### hack to determine if we need to free stuff
+ ret = ''
+ if self.builtinIndirection + self.arg.indirectionLevel > 1:
+ # memory returned into an OLECHAR should be freed
+ ret = "\tCoTaskMemFree(%s);\n" % self.arg.name
+ return ret + ArgFormatterPythonCOM.GetBuildForInterfacePostCode(self)
+
+class ArgFormatterSTATSTG(ArgFormatterPythonCOM):
+ def _GetPythonTypeDesc(self):
+ return ""
+ def GetParsePostCode(self):
+ return '\tif (!PyCom_PyObjectAsSTATSTG(ob%s, %s, 0/*flags*/)) bPythonIsHappy = FALSE;\n' % (self.arg.name, self.GetIndirectedArgName(None, 1))
+ def GetBuildForInterfacePreCode(self):
+ notdirected = self.GetIndirectedArgName(None, 1)
+ return "\tob%s = PyCom_PyObjectFromSTATSTG(%s);\n\t// STATSTG doco says our responsibility to free\n\tif ((%s).pwcsName) CoTaskMemFree((%s).pwcsName);\n" % (self.arg.name, self.GetIndirectedArgName(None, 1),notdirected,notdirected)
+
+class ArgFormatterGeneric(ArgFormatterPythonCOM):
+ def _GetPythonTypeDesc(self):
+ return "" % self.arg.type
+ def GetParsePostCode(self):
+ return '\tif (!PyObject_As%s(ob%s, &%s) bPythonIsHappy = FALSE;\n' % (self.arg.type, self.arg.name, self.GetIndirectedArgName(None, 1))
+ def GetInterfaceArgCleanup(self):
+ return '\tPyObject_Free%s(%s);\n' % (self.arg.type, self.arg.name)
+ def GetBuildForInterfacePreCode(self):
+ notdirected = self.GetIndirectedArgName(None, 1)
+ return "\tob%s = PyObject_From%s(%s);\n" % (self.arg.name, self.arg.type, self.GetIndirectedArgName(None, 1))
+
+class ArgFormatterIDLIST(ArgFormatterPythonCOM):
+ def _GetPythonTypeDesc(self):
+ return ""
+ def GetParsePostCode(self):
+ return '\tif (bPythonIsHappy && !PyObject_AsPIDL(ob%s, &%s)) bPythonIsHappy = FALSE;\n' % (self.arg.name, self.GetIndirectedArgName(None, 1))
+ def GetInterfaceArgCleanup(self):
+ return '\tPyObject_FreePIDL(%s);\n' % (self.arg.name,)
+ def GetBuildForInterfacePreCode(self):
+ notdirected = self.GetIndirectedArgName(None, 1)
+ return "\tob%s = PyObject_FromPIDL(%s);\n" % (self.arg.name, self.GetIndirectedArgName(None, 1))
+
+class ArgFormatterHANDLE(ArgFormatterPythonCOM):
+ def _GetPythonTypeDesc(self):
+ return ""
+ def GetParsePostCode(self):
+ return '\tif (!PyWinObject_AsHANDLE(ob%s, &%s, FALSE) bPythonIsHappy = FALSE;\n' % (self.arg.name, self.GetIndirectedArgName(None, 1))
+ def GetBuildForInterfacePreCode(self):
+ notdirected = self.GetIndirectedArgName(None, 1)
+ return "\tob%s = PyWinObject_FromHANDLE(%s);\n" % (self.arg.name, self.GetIndirectedArgName(None, 0))
+
+class ArgFormatterLARGE_INTEGER(ArgFormatterPythonCOM):
+ def GetKeyName(self):
+ return "LARGE_INTEGER"
+ def _GetPythonTypeDesc(self):
+ return "" % self.GetKeyName()
+ def GetParsePostCode(self):
+ return '\tif (!PyWinObject_As%s(ob%s, %s)) bPythonIsHappy = FALSE;\n' % (self.GetKeyName(), self.arg.name, self.GetIndirectedArgName(None, 1))
+ def GetBuildForInterfacePreCode(self):
+ notdirected = self.GetIndirectedArgName(None, 0)
+ return "\tob%s = PyWinObject_From%s(%s);\n" % (self.arg.name, self.GetKeyName(), notdirected)
+
+class ArgFormatterULARGE_INTEGER(ArgFormatterLARGE_INTEGER):
+ def GetKeyName(self):
+ return "ULARGE_INTEGER"
+
+class ArgFormatterInterface(ArgFormatterPythonCOM):
+ def GetInterfaceCppObjectInfo(self):
+ return self.GetIndirectedArgName(1, self.arg.indirectionLevel), \
+ "%s * %s" % (self.GetUnconstType(), self.arg.name)
+
+ def GetParsePostCode(self):
+ # This gets called for out params in gateway mode
+ if self.gatewayMode:
+ sArg = self.GetIndirectedArgName(None, 2)
+ else:
+ # vs. in params for interface mode.
+ sArg = self.GetIndirectedArgName(1, 2)
+ return "\tif (bPythonIsHappy && !PyCom_InterfaceFromPyInstanceOrObject(ob%s, IID_%s, (void **)%s, TRUE /* bNoneOK */))\n\t\t bPythonIsHappy = FALSE;\n" % (self.arg.name, self.arg.type, sArg)
+
+ def GetBuildForInterfacePreCode(self):
+ return "\tob%s = PyCom_PyObjectFromIUnknown(%s, IID_%s, FALSE);\n" % (self.arg.name, self.arg.name, self.arg.type)
+
+ def GetBuildForGatewayPreCode(self):
+ sPrefix = self._IndirectPrefix(self._GetDeclaredIndirection(), 1)
+ return "\tob%s = PyCom_PyObjectFromIUnknown(%s%s, IID_%s, TRUE);\n" % (self.arg.name, sPrefix, self.arg.name, self.arg.type)
+
+ def GetInterfaceArgCleanup(self):
+ return "\tif (%s) %s->Release();\n" % (self.arg.name, self.arg.name)
+
+class ArgFormatterVARIANT(ArgFormatterPythonCOM):
+ def GetParsePostCode(self):
+ return "\tif ( !PyCom_VariantFromPyObject(ob%s, %s) )\n\t\tbPythonIsHappy = FALSE;\n" % (self.arg.name, self.GetIndirectedArgName(None, 1))
+
+ def GetBuildForGatewayPreCode(self):
+ notdirected = self.GetIndirectedArgName(None, 1)
+ return "\tob%s = PyCom_PyObjectFromVariant(%s);\n" % (self.arg.name, notdirected)
+ def GetBuildForGatewayPostCode(self):
+ return "\tPy_XDECREF(ob%s);\n" % self.arg.name
+
+ # Key : , Python Type Description, ParseTuple format char
+ConvertSimpleTypes = {"BOOL":("BOOL", "int", "i"),
+ "UINT":("UINT", "int", "i"),
+ "BYTE": ("BYTE", "int", "i"),
+ "INT": ("INT", "int", "i"),
+ "DWORD": ("DWORD", "int", "l"),
+ "HRESULT":("HRESULT", "int", "l"),
+ "ULONG": ("ULONG", "int", "l"),
+ "LONG": ("LONG", "int", "l"),
+ "int": ("int", "int", "i"),
+ "long": ("long", "int", "l"),
+ "DISPID": ("DISPID", "long", "l"),
+ "APPBREAKFLAGS": ("int", "int", "i"),
+ "BREAKRESUMEACTION": ("int", "int", "i"),
+ "ERRORRESUMEACTION": ("int", "int", "i"),
+ "BREAKREASON": ("int", "int", "i"),
+ "BREAKPOINT_STATE": ("int", "int", "i"),
+ "BREAKRESUME_ACTION": ("int", "int", "i"),
+ "SOURCE_TEXT_ATTR": ("int", "int", "i"),
+ "TEXT_DOC_ATTR": ("int", "int", "i"),
+ "QUERYOPTION": ("int", "int", "i"),
+ "PARSEACTION": ("int", "int", "i"),
+}
+
+class ArgFormatterSimple(ArgFormatter):
+ """An arg formatter for simple integer etc types"""
+ def GetFormatChar(self):
+ return ConvertSimpleTypes[self.arg.type][2]
+ def _GetPythonTypeDesc(self):
+ return ConvertSimpleTypes[self.arg.type][1]
+
+AllConverters = {"const OLECHAR": (ArgFormatterOLECHAR, 0, 1),
+ "WCHAR": (ArgFormatterOLECHAR, 0, 1),
+ "OLECHAR": (ArgFormatterOLECHAR, 0, 1),
+ "LPCOLESTR": (ArgFormatterOLECHAR, 1, 1),
+ "LPOLESTR": (ArgFormatterOLECHAR, 1, 1),
+ "LPCWSTR": (ArgFormatterOLECHAR, 1, 1),
+ "LPWSTR": (ArgFormatterOLECHAR, 1, 1),
+ "LPCSTR": (ArgFormatterOLECHAR, 1, 1),
+ "LPTSTR": (ArgFormatterTCHAR, 1, 1),
+ "LPCTSTR": (ArgFormatterTCHAR, 1, 1),
+ "HANDLE": (ArgFormatterHANDLE, 0),
+ "BSTR": (ArgFormatterBSTR, 1, 0),
+ "const IID": (ArgFormatterIID, 0),
+ "CLSID": (ArgFormatterIID, 0),
+ "IID": (ArgFormatterIID, 0),
+ "GUID": (ArgFormatterIID, 0),
+ "const GUID": (ArgFormatterIID, 0),
+ "const IID": (ArgFormatterIID, 0),
+ "REFCLSID": (ArgFormatterIID, 0),
+ "REFIID": (ArgFormatterIID, 0),
+ "REFGUID": (ArgFormatterIID, 0),
+ "const FILETIME": (ArgFormatterTime, 0),
+ "const SYSTEMTIME":(ArgFormatterTime, 0),
+ "const LPSYSTEMTIME":(ArgFormatterTime, 1, 1),
+ "LPSYSTEMTIME": (ArgFormatterTime, 1, 1),
+ "FILETIME": (ArgFormatterTime, 0),
+ "SYSTEMTIME": (ArgFormatterTime, 0),
+ "STATSTG": (ArgFormatterSTATSTG, 0),
+ "LARGE_INTEGER": (ArgFormatterLARGE_INTEGER, 0),
+ "ULARGE_INTEGER": (ArgFormatterULARGE_INTEGER, 0),
+ "VARIANT": (ArgFormatterVARIANT, 0),
+ "float": (ArgFormatterFloat, 0),
+ "single": (ArgFormatterFloat, 0),
+ "short": (ArgFormatterShort, 0),
+ "WORD": (ArgFormatterShort, 0),
+ "VARIANT_BOOL": (ArgFormatterShort, 0),
+ "HWND": (ArgFormatterLONG_PTR, 1),
+ "HMENU": (ArgFormatterLONG_PTR, 1),
+ "HOLEMENU": (ArgFormatterLONG_PTR, 1),
+ "HICON": (ArgFormatterLONG_PTR, 1),
+ "HDC": (ArgFormatterLONG_PTR, 1),
+ "LPARAM": (ArgFormatterLONG_PTR, 1),
+ "WPARAM": (ArgFormatterLONG_PTR, 1),
+ "LRESULT": (ArgFormatterLONG_PTR, 1),
+ "UINT": (ArgFormatterShort, 0),
+ "SVSIF": (ArgFormatterShort, 0),
+ "Control": (ArgFormatterInterface, 0, 1),
+ "DataObject": (ArgFormatterInterface, 0, 1),
+ "_PropertyBag": (ArgFormatterInterface, 0, 1),
+ "AsyncProp": (ArgFormatterInterface, 0, 1),
+ "DataSource": (ArgFormatterInterface, 0, 1),
+ "DataFormat": (ArgFormatterInterface, 0, 1),
+ "void **": (ArgFormatterInterface, 2, 2),
+ "ITEMIDLIST": (ArgFormatterIDLIST, 0, 0),
+ "LPITEMIDLIST": (ArgFormatterIDLIST, 0, 1),
+ "LPCITEMIDLIST": (ArgFormatterIDLIST, 0, 1),
+ "const ITEMIDLIST": (ArgFormatterIDLIST, 0, 1),
+}
+
+# Auto-add all the simple types
+for key in ConvertSimpleTypes.keys():
+ AllConverters[key] = ArgFormatterSimple, 0
+
+def make_arg_converter(arg):
+ try:
+ clz = AllConverters[arg.type][0]
+ bin = AllConverters[arg.type][1]
+ decl = 0
+ if len(AllConverters[arg.type])>2:
+ decl = AllConverters[arg.type][2]
+ return clz(arg,bin, decl)
+ except KeyError:
+ if arg.type[0]=="I":
+ return ArgFormatterInterface(arg, 0, 1)
+
+ raise error_not_supported("The type '%s' (%s) is unknown." % (arg.type, arg.name))
+
+
+#############################################################
+#
+# The instances that represent the args, methods and interface
+class Argument:
+ """A representation of an argument to a COM method
+
+ This class contains information about a specific argument to a method.
+ In addition, methods exist so that an argument knows how to convert itself
+ to/from Python arguments.
+ """
+# in,out type name [ ]
+# -------------- -------- ------------ ------
+ regex = re.compile(r'/\* \[([^\]]*.*?)] \*/[ \t](.*[* ]+)(\w+)(\[ *])?[\),]')
+ def __init__(self, good_interface_names):
+ self.good_interface_names = good_interface_names
+ self.inout = self.name = self.type = None
+ self.const = 0
+ self.arrayDecl = 0
+ def BuildFromFile(self, file):
+ """Parse and build my data from a file
+
+ Reads the next line in the file, and matches it as an argument
+ description. If not a valid argument line, an error_not_found exception
+ is raised.
+ """
+ line = file.readline()
+ mo = self.regex.search(line)
+ if not mo:
+ raise error_not_found
+ self.name = mo.group(3)
+ self.inout = mo.group(1).split('][')
+ typ = mo.group(2).strip()
+ self.raw_type = typ
+ self.indirectionLevel = 0
+ if mo.group(4): # Has "[ ]" decl
+ self.arrayDecl = 1
+ try:
+ pos = typ.rindex("__RPC_FAR")
+ self.indirectionLevel = self.indirectionLevel + 1
+ typ = typ[:pos].strip()
+ except ValueError:
+ pass
+
+ typ = typ.replace("__RPC_FAR", "")
+ while 1:
+ try:
+ pos = typ.rindex("*")
+ self.indirectionLevel = self.indirectionLevel + 1
+ typ = typ[:pos].strip()
+ except ValueError:
+ break
+ self.type = typ
+ if self.type[:6]=="const ":
+ self.unc_type = self.type[6:]
+ else:
+ self.unc_type = self.type
+
+ if VERBOSE:
+ print(" Arg %s of type %s%s (%s)" % (self.name, self.type, "*" * self.indirectionLevel, self.inout))
+
+ def HasAttribute(self, typ):
+ """Determines if the argument has the specific attribute.
+
+ Argument attributes are specified in the header file, such as
+ "[in][out][retval]" etc. You can pass a specific string (eg "out")
+ to find if this attribute was specified for the argument
+ """
+ return typ in self.inout
+
+ def GetRawDeclaration(self):
+ ret = "%s %s" % (self.raw_type, self.name)
+ if self.arrayDecl:
+ ret = ret + "[]"
+ return ret
+
+class Method:
+ """A representation of a C++ method on a COM interface
+
+ This class contains information about a specific method, as well as
+ a list of all @Argument@s
+ """
+# options ret type callconv name
+# ----------------- -------- -------- --------
+ regex = re.compile(r'virtual (/\*.*?\*/ )?(.*?) (.*?) (.*?)\(\w?')
+ def __init__(self, good_interface_names):
+ self.good_interface_names = good_interface_names
+ self.name = self.result = self.callconv = None
+ self.args = []
+ def BuildFromFile(self, file):
+ """Parse and build my data from a file
+
+ Reads the next line in the file, and matches it as a method
+ description. If not a valid method line, an error_not_found exception
+ is raised.
+ """
+ line = file.readline()
+ mo = self.regex.search(line)
+ if not mo:
+ raise error_not_found
+ self.name = mo.group(4)
+ self.result = mo.group(2)
+ if self.result != "HRESULT":
+ if self.result=="DWORD": # DWORD is for old old stuff?
+ print("Warning: Old style interface detected - compilation errors likely!")
+ else:
+ print("Method %s - Only HRESULT return types are supported." % self.name)
+# raise error_not_supported, if VERBOSE:
+ print(" Method %s %s(" % (self.result, self.name))
+ while 1:
+ arg = Argument(self.good_interface_names)
+ try:
+ arg.BuildFromFile(file)
+ self.args.append(arg)
+ except error_not_found:
+ break
+
+class Interface:
+ """A representation of a C++ COM Interface
+
+ This class contains information about a specific interface, as well as
+ a list of all @Method@s
+ """
+# name base
+# -------- --------
+ regex = re.compile("(interface|) ([^ ]*) : public (.*)$")
+ def __init__(self, mo):
+ self.methods = []
+ self.name = mo.group(2)
+ self.base = mo.group(3)
+ if VERBOSE:
+ print("Interface %s : public %s" % (self.name, self.base))
+
+ def BuildMethods(self, file):
+ """Build all sub-methods for this interface"""
+ # skip the next 2 lines.
+ file.readline();file.readline();
+ while 1:
+ try:
+ method = Method([self.name])
+ method.BuildFromFile(file)
+ self.methods.append(method)
+ except error_not_found:
+ break
+
+def find_interface(interfaceName, file):
+ """Find and return an interface in a file
+
+ Given an interface name and file, search for the specified interface.
+
+ Upon return, the interface itself has been built,
+ but not the methods.
+ """
+ interface = None
+ line = file.readline()
+ while line:
+ mo = Interface.regex.search(line)
+ if mo:
+ name = mo.group(2)
+ print(name)
+ AllConverters[name] = (ArgFormatterInterface, 0, 1)
+ if name==interfaceName:
+ interface = Interface(mo)
+ interface.BuildMethods(file)
+ line = file.readline()
+ if interface:
+ return interface
+ raise error_not_found
+
+
+def parse_interface_info(interfaceName, file):
+ """Find, parse and return an interface in a file
+
+ Given an interface name and file, search for the specified interface.
+
+ Upon return, the interface itself is fully built,
+ """
+ try:
+ return find_interface(interfaceName, file)
+ except re.error:
+ traceback.print_exc()
+ print("The interface could not be built, as the regular expression failed!")
+def test():
+ f=open("d:\\msdev\\include\\objidl.h")
+ try:
+ parse_interface_info("IPersistStream", f)
+ finally:
+ f.close()
+
+def test_regex(r,text):
+ res=r.search(text,0)
+ if res==-1:
+ print("** Not found")
+ else:
+ print("%d\n%s\n%s\n%s\n%s" % (res, r.group(1), r.group(2), r.group(3), r.group(4)))
diff --git a/Lib/site-packages/win32com/olectl.py b/Lib/site-packages/win32com/olectl.py
new file mode 100644
index 0000000..b112a5c
--- /dev/null
+++ b/Lib/site-packages/win32com/olectl.py
@@ -0,0 +1,68 @@
+"""Constants used by COM Controls
+
+ Hand created version of OLECTL.H constants.
+"""
+
+import winerror
+
+FACILITY_CONTROL = 0xa
+
+def MAKE_SCODE(sev, fac, code):
+ return int((int(-sev)<<31) | ((fac)<<16) | ((code)))
+
+def STD_CTL_SCODE(n):
+ return MAKE_SCODE(winerror.SEVERITY_ERROR, FACILITY_CONTROL, n)
+
+CTL_E_ILLEGALFUNCTIONCALL = STD_CTL_SCODE(5)
+CTL_E_OVERFLOW = STD_CTL_SCODE(6)
+CTL_E_OUTOFMEMORY = STD_CTL_SCODE(7)
+CTL_E_DIVISIONBYZERO = STD_CTL_SCODE(11)
+CTL_E_OUTOFSTRINGSPACE = STD_CTL_SCODE(14)
+CTL_E_OUTOFSTACKSPACE = STD_CTL_SCODE(28)
+CTL_E_BADFILENAMEORNUMBER = STD_CTL_SCODE(52)
+CTL_E_FILENOTFOUND = STD_CTL_SCODE(53)
+CTL_E_BADFILEMODE = STD_CTL_SCODE(54)
+CTL_E_FILEALREADYOPEN = STD_CTL_SCODE(55)
+CTL_E_DEVICEIOERROR = STD_CTL_SCODE(57)
+CTL_E_FILEALREADYEXISTS = STD_CTL_SCODE(58)
+CTL_E_BADRECORDLENGTH = STD_CTL_SCODE(59)
+CTL_E_DISKFULL = STD_CTL_SCODE(61)
+CTL_E_BADRECORDNUMBER = STD_CTL_SCODE(63)
+CTL_E_BADFILENAME = STD_CTL_SCODE(64)
+CTL_E_TOOMANYFILES = STD_CTL_SCODE(67)
+CTL_E_DEVICEUNAVAILABLE = STD_CTL_SCODE(68)
+CTL_E_PERMISSIONDENIED = STD_CTL_SCODE(70)
+CTL_E_DISKNOTREADY = STD_CTL_SCODE(71)
+CTL_E_PATHFILEACCESSERROR = STD_CTL_SCODE(75)
+CTL_E_PATHNOTFOUND = STD_CTL_SCODE(76)
+CTL_E_INVALIDPATTERNSTRING = STD_CTL_SCODE(93)
+CTL_E_INVALIDUSEOFNULL = STD_CTL_SCODE(94)
+CTL_E_INVALIDFILEFORMAT = STD_CTL_SCODE(321)
+CTL_E_INVALIDPROPERTYVALUE = STD_CTL_SCODE(380)
+CTL_E_INVALIDPROPERTYARRAYINDEX = STD_CTL_SCODE(381)
+CTL_E_SETNOTSUPPORTEDATRUNTIME = STD_CTL_SCODE(382)
+CTL_E_SETNOTSUPPORTED = STD_CTL_SCODE(383)
+CTL_E_NEEDPROPERTYARRAYINDEX = STD_CTL_SCODE(385)
+CTL_E_SETNOTPERMITTED = STD_CTL_SCODE(387)
+CTL_E_GETNOTSUPPORTEDATRUNTIME = STD_CTL_SCODE(393)
+CTL_E_GETNOTSUPPORTED = STD_CTL_SCODE(394)
+CTL_E_PROPERTYNOTFOUND = STD_CTL_SCODE(422)
+CTL_E_INVALIDCLIPBOARDFORMAT = STD_CTL_SCODE(460)
+CTL_E_INVALIDPICTURE = STD_CTL_SCODE(481)
+CTL_E_PRINTERERROR = STD_CTL_SCODE(482)
+CTL_E_CANTSAVEFILETOTEMP = STD_CTL_SCODE(735)
+CTL_E_SEARCHTEXTNOTFOUND = STD_CTL_SCODE(744)
+CTL_E_REPLACEMENTSTOOLONG = STD_CTL_SCODE(746)
+
+CONNECT_E_FIRST = MAKE_SCODE(winerror.SEVERITY_ERROR, winerror.FACILITY_ITF, 0x0200)
+CONNECT_E_LAST = MAKE_SCODE(winerror.SEVERITY_ERROR, winerror.FACILITY_ITF, 0x020F)
+CONNECT_S_FIRST = MAKE_SCODE(winerror.SEVERITY_SUCCESS, winerror.FACILITY_ITF, 0x0200)
+CONNECT_S_LAST = MAKE_SCODE(winerror.SEVERITY_SUCCESS, winerror.FACILITY_ITF, 0x020F)
+
+CONNECT_E_NOCONNECTION = CONNECT_E_FIRST+0
+CONNECT_E_ADVISELIMIT = CONNECT_E_FIRST+1
+CONNECT_E_CANNOTCONNECT = CONNECT_E_FIRST+2
+CONNECT_E_OVERRIDDEN = CONNECT_E_FIRST+3
+
+CLASS_E_NOTLICENSED = winerror.CLASSFACTORY_E_FIRST+2
+
diff --git a/Lib/site-packages/win32com/readme.htm b/Lib/site-packages/win32com/readme.htm
new file mode 100644
index 0000000..0112b0c
--- /dev/null
+++ b/Lib/site-packages/win32com/readme.htm
@@ -0,0 +1,87 @@
+
+
+
+ win32com Readme
+
+
+
+
The win32com/test directory contains some interesting
+ scripts (and a new readme.txt). Although these
+ are used for testing, they do show a variety of COM techniques.
+
+
VARIANT objects
+
win32com.client now has explicit VARIANT objects which can be used in
+situations where you need more control over the argument types passed when
+calling COM methods. See the documentation on
+this object
+
+
+In all builds prior to 204, a COM currency value was returned as a tuple of
+integers. Working with 2 integers to represent a currency object was a poor
+choice, but the alternative was never clear. Now Python ships with the
+decimal
+module, the alternative has arrived!
+
+
+Up until build 212, code could set pythoncom.__future_currency__ = True
+to force use of the decimal module, with a warning issued otherwise. In
+builds 213 and later, the decimal module is unconditionally used when
+pythoncon returns you a currency value.
+
+
+
Recent Changes
+
+
Lots of internal changes on the road to py3k
+
+
win32com.axcontrol and win2con.internet
+Many more interfaces for hosting AX controls and the interfaces
+used by Internet Explorer.
+
+
win32com.shell
+The shell interfaces have undergone a number of enhancements and changes.
+A couple of methods have changed signature between the first build with shell support (200) and later builds.
+SHGetFileInfo was broken in its result handling, so had to be changed - this
+is the only function used by the samples that changed, but others not used by the samples also have changed.
+These shell interfaces are now generally stable.
+
New win32com.taskscheduler module
+Roger Upole has contributed an interface to the Windows task scheduler. This is actually very neat, and it allows
+Python to edit the task list as shown by Windows Control Panel. Property page suppport may even appear later,
+now that the win32 library has the new win32rcparser module.
+
ActiveX Scripting
+
+
Python only supports "trusted" execution hosts - thus, it will no longer work
+as an engine inside IE (Python itself no longer has a restricted execution environment).
+Python continues to work fine as an Active Scripting Engine in all other
+applications, including Windows Scripting Host, and ASP.
+
+
There is also support for Python as an ActiveX Scripting Host.
+
+
Active Debugging seems to be fully functional.
+
+
Older stuff
+
+
+
Unexpected exceptions in Python COM objects will generally now dump
+the exception and traceback to stdout. This is useful for debugging
+and testing - it means that in some cases there will be no need to register
+an object with --debug to see these
+tracebacks. Note that COM objects used by server processes (such as
+ASP) generally have no valid stdout, so will still need to use --debug as usual.
+
+
universal gateway support has been improved - we can now work as an
+Outlook Addin
+
+
+
+
diff --git a/Lib/site-packages/win32com/server/__init__.py b/Lib/site-packages/win32com/server/__init__.py
new file mode 100644
index 0000000..143e1ba
--- /dev/null
+++ b/Lib/site-packages/win32com/server/__init__.py
@@ -0,0 +1 @@
+# Empty __init__ file to designate a sub-package.
\ No newline at end of file
diff --git a/Lib/site-packages/win32com/server/connect.py b/Lib/site-packages/win32com/server/connect.py
new file mode 100644
index 0000000..6210403
--- /dev/null
+++ b/Lib/site-packages/win32com/server/connect.py
@@ -0,0 +1,65 @@
+"""Utilities for Server Side connections.
+
+ A collection of helpers for server side connection points.
+"""
+import pythoncom
+from .exception import Exception
+import winerror
+from win32com import olectl
+import win32com.server.util
+
+# Methods implemented by the interfaces.
+IConnectionPointContainer_methods = ["EnumConnectionPoints","FindConnectionPoint"]
+IConnectionPoint_methods = ["EnumConnections","Unadvise","Advise","GetConnectionPointContainer","GetConnectionInterface"]
+
+class ConnectableServer:
+ _public_methods_ = IConnectionPointContainer_methods + IConnectionPoint_methods
+ _com_interfaces_ = [pythoncom.IID_IConnectionPoint, pythoncom.IID_IConnectionPointContainer]
+ # Clients must set _connect_interfaces_ = [...]
+ def __init__(self):
+ self.cookieNo = 0
+ self.connections = {}
+ # IConnectionPoint interfaces
+ def EnumConnections(self):
+ raise Exception(winerror.E_NOTIMPL)
+ def GetConnectionInterface(self):
+ raise Exception(winerror.E_NOTIMPL)
+ def GetConnectionPointContainer(self):
+ return win32com.server.util.wrap(self)
+ def Advise(self, pUnk):
+ # Creates a connection to the client. Simply allocate a new cookie,
+ # find the clients interface, and store it in a dictionary.
+ try:
+ interface = pUnk.QueryInterface(self._connect_interfaces_[0],pythoncom.IID_IDispatch)
+ except pythoncom.com_error:
+ raise Exception(scode=olectl.CONNECT_E_NOCONNECTION)
+ self.cookieNo = self.cookieNo + 1
+ self.connections[self.cookieNo] = interface
+ return self.cookieNo
+ def Unadvise(self, cookie):
+ # Destroy a connection - simply delete interface from the map.
+ try:
+ del self.connections[cookie]
+ except KeyError:
+ raise Exception(scode=winerror.E_UNEXPECTED)
+ # IConnectionPointContainer interfaces
+ def EnumConnectionPoints(self):
+ raise Exception(winerror.E_NOTIMPL)
+ def FindConnectionPoint(self, iid):
+ # Find a connection we support. Only support the single event interface.
+ if iid in self._connect_interfaces_:
+ return win32com.server.util.wrap(self)
+
+ def _BroadcastNotify(self, broadcaster, extraArgs):
+ # Broadcasts a notification to all connections.
+ # Ignores clients that fail.
+ for interface in self.connections.values():
+ try:
+ broadcaster(*(interface,)+extraArgs)
+ except pythoncom.com_error as details:
+ self._OnNotifyFail(interface, details)
+
+ def _OnNotifyFail(self, interface, details):
+ print("Ignoring COM error to connection - %s" % (repr(details)))
+
+
diff --git a/Lib/site-packages/win32com/server/dispatcher.py b/Lib/site-packages/win32com/server/dispatcher.py
new file mode 100644
index 0000000..f9fde86
--- /dev/null
+++ b/Lib/site-packages/win32com/server/dispatcher.py
@@ -0,0 +1,270 @@
+"""Dispatcher
+
+Please see policy.py for a discussion on dispatchers and policies
+"""
+import pythoncom, traceback, win32api
+from sys import exc_info
+
+#
+from win32com.server.exception import IsCOMServerException
+from win32com.util import IIDToInterfaceName
+import win32com
+
+class DispatcherBase:
+ """ The base class for all Dispatchers.
+
+ This dispatcher supports wrapping all operations in exception handlers,
+ and all the necessary delegation to the policy.
+
+ This base class supports the printing of "unexpected" exceptions. Note, however,
+ that exactly where the output of print goes may not be useful! A derived class may
+ provide additional semantics for this.
+ """
+ def __init__(self, policyClass, object):
+ self.policy = policyClass(object)
+ # The logger we should dump to. If None, we should send to the
+ # default location (typically 'print')
+ self.logger = getattr(win32com, "logger", None)
+
+ # Note the "return self._HandleException_()" is purely to stop pychecker
+ # complaining - _HandleException_ will itself raise an exception for the
+ # pythoncom framework, so the result will never be seen.
+ def _CreateInstance_(self, clsid, reqIID):
+ try:
+ self.policy._CreateInstance_(clsid, reqIID)
+ return pythoncom.WrapObject(self, reqIID)
+ except:
+ return self._HandleException_()
+
+ def _QueryInterface_(self, iid):
+ try:
+ return self.policy._QueryInterface_(iid)
+ except:
+ return self._HandleException_()
+
+ def _Invoke_(self, dispid, lcid, wFlags, args):
+ try:
+ return self.policy._Invoke_(dispid, lcid, wFlags, args)
+ except:
+ return self._HandleException_()
+
+ def _GetIDsOfNames_(self, names, lcid):
+ try:
+ return self.policy._GetIDsOfNames_(names, lcid)
+ except:
+ return self._HandleException_()
+
+ def _GetTypeInfo_(self, index, lcid):
+ try:
+ return self.policy._GetTypeInfo_(index, lcid)
+ except:
+ return self._HandleException_()
+
+ def _GetTypeInfoCount_(self):
+ try:
+ return self.policy._GetTypeInfoCount_()
+ except:
+ return self._HandleException_()
+
+ def _GetDispID_(self, name, fdex):
+ try:
+ return self.policy._GetDispID_(name, fdex)
+ except:
+ return self._HandleException_()
+
+ def _InvokeEx_(self, dispid, lcid, wFlags, args, kwargs, serviceProvider):
+ try:
+ return self.policy._InvokeEx_(dispid, lcid, wFlags, args, kwargs, serviceProvider)
+ except:
+ return self._HandleException_()
+
+ def _DeleteMemberByName_(self, name, fdex):
+ try:
+ return self.policy._DeleteMemberByName_(name, fdex)
+ except:
+ return self._HandleException_()
+
+ def _DeleteMemberByDispID_(self, id):
+ try:
+ return self.policy._DeleteMemberByDispID_(id)
+ except:
+ return self._HandleException_()
+
+ def _GetMemberProperties_(self, id, fdex):
+ try:
+ return self.policy._GetMemberProperties_(id, fdex)
+ except:
+ return self._HandleException_()
+
+ def _GetMemberName_(self, dispid):
+ try:
+ return self.policy._GetMemberName_(dispid)
+ except:
+ return self._HandleException_()
+
+ def _GetNextDispID_(self, fdex, flags):
+ try:
+ return self.policy._GetNextDispID_(fdex, flags)
+ except:
+ return self._HandleException_()
+
+ def _GetNameSpaceParent_(self):
+ try:
+ return self.policy._GetNameSpaceParent_()
+ except:
+ return self._HandleException_()
+
+ def _HandleException_(self):
+ """Called whenever an exception is raised.
+
+ Default behaviour is to print the exception.
+ """
+ # If not a COM exception, print it for the developer.
+ if not IsCOMServerException():
+ if self.logger is not None:
+ self.logger.exception("pythoncom server error")
+ else:
+ traceback.print_exc()
+ # But still raise it for the framework.
+ raise
+
+ def _trace_(self, *args):
+ if self.logger is not None:
+ record = " ".join(map(str, args))
+ self.logger.debug(record)
+ else:
+ for arg in args[:-1]:
+ print(arg, end=' ')
+ print(args[-1])
+
+class DispatcherTrace(DispatcherBase):
+ """A dispatcher, which causes a 'print' line for each COM function called.
+ """
+ def _QueryInterface_(self, iid):
+ rc = DispatcherBase._QueryInterface_(self, iid)
+ if not rc:
+ self._trace_("in %s._QueryInterface_ with unsupported IID %s (%s)" % (repr(self.policy._obj_), IIDToInterfaceName(iid),iid))
+ return rc
+
+ def _GetIDsOfNames_(self, names, lcid):
+ self._trace_("in _GetIDsOfNames_ with '%s' and '%d'\n" % (names, lcid))
+ return DispatcherBase._GetIDsOfNames_(self, names, lcid)
+
+ def _GetTypeInfo_(self, index, lcid):
+ self._trace_("in _GetTypeInfo_ with index=%d, lcid=%d\n" % (index, lcid))
+ return DispatcherBase._GetTypeInfo_(self, index, lcid)
+
+ def _GetTypeInfoCount_(self):
+ self._trace_("in _GetTypeInfoCount_\n")
+ return DispatcherBase._GetTypeInfoCount_(self)
+
+ def _Invoke_(self, dispid, lcid, wFlags, args):
+ self._trace_("in _Invoke_ with", dispid, lcid, wFlags, args)
+ return DispatcherBase._Invoke_(self, dispid, lcid, wFlags, args)
+
+ def _GetDispID_(self, name, fdex):
+ self._trace_("in _GetDispID_ with", name, fdex)
+ return DispatcherBase._GetDispID_(self, name, fdex)
+
+ def _InvokeEx_(self, dispid, lcid, wFlags, args, kwargs, serviceProvider):
+ self._trace_("in %r._InvokeEx_-%s%r [%x,%s,%r]" % (self.policy._obj_, dispid, args, wFlags, lcid, serviceProvider))
+ return DispatcherBase._InvokeEx_(self, dispid, lcid, wFlags, args, kwargs, serviceProvider)
+
+ def _DeleteMemberByName_(self, name, fdex):
+ self._trace_("in _DeleteMemberByName_ with", name, fdex)
+ return DispatcherBase._DeleteMemberByName_(self, name, fdex)
+
+ def _DeleteMemberByDispID_(self, id):
+ self._trace_("in _DeleteMemberByDispID_ with", id)
+ return DispatcherBase._DeleteMemberByDispID_(self, id)
+
+ def _GetMemberProperties_(self, id, fdex):
+ self._trace_("in _GetMemberProperties_ with", id, fdex)
+ return DispatcherBase._GetMemberProperties_(self, id, fdex)
+
+ def _GetMemberName_(self, dispid):
+ self._trace_("in _GetMemberName_ with", dispid)
+ return DispatcherBase._GetMemberName_(self, dispid)
+
+ def _GetNextDispID_(self, fdex, flags):
+ self._trace_("in _GetNextDispID_ with", fdex, flags)
+ return DispatcherBase._GetNextDispID_(self, fdex, flags)
+
+ def _GetNameSpaceParent_(self):
+ self._trace_("in _GetNameSpaceParent_")
+ return DispatcherBase._GetNameSpaceParent_(self)
+
+
+class DispatcherWin32trace(DispatcherTrace):
+ """A tracing dispatcher that sends its output to the win32trace remote collector.
+
+ """
+ def __init__(self, policyClass, object):
+ DispatcherTrace.__init__(self, policyClass, object)
+ if self.logger is None:
+ # If we have no logger, setup our output.
+ import win32traceutil # Sets up everything.
+ self._trace_("Object with win32trace dispatcher created (object=%s)" % repr(object))
+
+
+class DispatcherOutputDebugString(DispatcherTrace):
+ """A tracing dispatcher that sends its output to win32api.OutputDebugString
+
+ """
+ def _trace_(self, *args):
+ for arg in args[:-1]:
+ win32api.OutputDebugString(str(arg)+" ")
+ win32api.OutputDebugString(str(args[-1])+"\n")
+
+
+class DispatcherWin32dbg(DispatcherBase):
+ """A source-level debugger dispatcher
+
+ A dispatcher which invokes the debugger as an object is instantiated, or
+ when an unexpected exception occurs.
+
+ Requires Pythonwin.
+ """
+ def __init__(self, policyClass, ob):
+ # No one uses this, and it just causes py2exe to drag all of
+ # pythonwin in.
+ #import pywin.debugger
+ pywin.debugger.brk()
+ print("The DispatcherWin32dbg dispatcher is deprecated!")
+ print("Please let me know if this is a problem.")
+ print("Uncomment the relevant lines in dispatcher.py to re-enable")
+ # DEBUGGER Note - You can either:
+ # * Hit Run and wait for a (non Exception class) exception to occur!
+ # * Set a breakpoint and hit run.
+ # * Step into the object creation (a few steps away!)
+ DispatcherBase.__init__(self, policyClass, ob)
+
+ def _HandleException_(self):
+ """ Invoke the debugger post mortem capability """
+ # Save details away.
+ typ, val, tb = exc_info()
+ #import pywin.debugger, pywin.debugger.dbgcon
+ debug = 0
+ try:
+ raise typ(val)
+ except Exception: # AARG - What is this Exception???
+ # Use some inside knowledge to borrow a Debugger option which dictates if we
+ # stop at "expected" exceptions.
+ debug = pywin.debugger.GetDebugger().get_option(pywin.debugger.dbgcon.OPT_STOP_EXCEPTIONS)
+ except:
+ debug = 1
+ if debug:
+ try:
+ pywin.debugger.post_mortem(tb, typ, val) # The original exception
+ except:
+ traceback.print_exc()
+
+ # But still raise it.
+ del tb
+ raise
+
+try:
+ import win32trace
+ DefaultDebugDispatcher = DispatcherWin32trace
+except ImportError: # no win32trace module - just use a print based one.
+ DefaultDebugDispatcher = DispatcherTrace
diff --git a/Lib/site-packages/win32com/server/exception.py b/Lib/site-packages/win32com/server/exception.py
new file mode 100644
index 0000000..ad32dd3
--- /dev/null
+++ b/Lib/site-packages/win32com/server/exception.py
@@ -0,0 +1,91 @@
+"""Exception Handling
+
+ Exceptions
+
+ To better support COM exceptions, the framework allows for an instance to be
+ raised. This instance may have a certain number of known attributes, which are
+ translated into COM exception details.
+
+ This means, for example, that Python could raise a COM exception that includes details
+ on a Help file and location, and a description for the user.
+
+ This module provides a class which provides the necessary attributes.
+
+"""
+import sys, pythoncom
+
+# Note that we derive from com_error, which derives from exceptions.Exception
+# Also note that we dont support "self.args", as we dont support tuple-unpacking
+class COMException(pythoncom.com_error):
+ """An Exception object that is understood by the framework.
+
+ If the framework is presented with an exception of type class,
+ it looks for certain known attributes on this class to provide rich
+ error information to the caller.
+
+ It should be noted that the framework supports providing this error
+ information via COM Exceptions, or via the ISupportErrorInfo interface.
+
+ By using this class, you automatically provide rich error information to the
+ server.
+ """
+ def __init__(self, description = None, scode = None,
+ source = None, helpfile = None, helpContext = None,
+ desc = None, hresult = None):
+ """Initialize an exception
+ **Params**
+
+ description -- A string description for the exception.
+ scode -- An integer scode to be returned to the server, if necessary.
+ The pythoncom framework defaults this to be DISP_E_EXCEPTION if not specified otherwise.
+ source -- A string which identifies the source of the error.
+ helpfile -- A string which points to a help file which contains details on the error.
+ helpContext -- An integer context in the help file.
+ desc -- A short-cut for description.
+ hresult -- A short-cut for scode.
+ """
+
+ # convert a WIN32 error into an HRESULT
+ scode = scode or hresult
+ if scode and scode != 1: # We dont want S_FALSE mapped!
+ if scode >= -32768 and scode < 32768:
+ # this is HRESULT_FROM_WIN32()
+ scode = -2147024896 | (scode & 0x0000FFFF)
+ self.scode = scode
+
+ self.description = description or desc
+ if scode==1 and not self.description:
+ self.description = "S_FALSE"
+ elif scode and not self.description:
+ self.description = pythoncom.GetScodeString(scode)
+
+ self.source = source
+ self.helpfile = helpfile
+ self.helpcontext = helpContext
+
+ # todo - fill in the exception value
+ pythoncom.com_error.__init__(self, scode, self.description, None, -1)
+
+ def __repr__(self):
+ return "" % (self.scode, self.description)
+
+# Old name for the COMException class.
+# Do NOT use the name Exception, as it is now a built-in
+# COMException is the new, official name.
+Exception = COMException
+
+def IsCOMException(t = None):
+ if t is None:
+ t = sys.exc_info()[0]
+ try:
+ return issubclass(t, pythoncom.com_error)
+ except TypeError: # 1.5 in -X mode?
+ return t is pythoncon.com_error
+
+def IsCOMServerException(t = None):
+ if t is None:
+ t = sys.exc_info()[0]
+ try:
+ return issubclass(t, COMException)
+ except TypeError: # String exception
+ return 0
diff --git a/Lib/site-packages/win32com/server/factory.py b/Lib/site-packages/win32com/server/factory.py
new file mode 100644
index 0000000..0fd0d15
--- /dev/null
+++ b/Lib/site-packages/win32com/server/factory.py
@@ -0,0 +1,22 @@
+# Class factory utilities.
+import pythoncom
+
+def RegisterClassFactories(clsids, flags = None, clsctx = None):
+ """Given a list of CLSID, create and register class factories.
+
+ Returns a list, which should be passed to RevokeClassFactories
+ """
+ if flags is None: flags = pythoncom.REGCLS_MULTIPLEUSE|pythoncom.REGCLS_SUSPENDED
+ if clsctx is None: clsctx = pythoncom.CLSCTX_LOCAL_SERVER
+ ret = []
+ for clsid in clsids:
+ # Some server append '-Embedding' etc
+ if clsid[0] not in ['-', '/']:
+ factory = pythoncom.MakePyFactory(clsid)
+ regId = pythoncom.CoRegisterClassObject(clsid, factory, clsctx, flags)
+ ret.append((factory, regId))
+ return ret
+
+def RevokeClassFactories(infos):
+ for factory, revokeId in infos:
+ pythoncom.CoRevokeClassObject(revokeId)
diff --git a/Lib/site-packages/win32com/server/localserver.py b/Lib/site-packages/win32com/server/localserver.py
new file mode 100644
index 0000000..baf5464
--- /dev/null
+++ b/Lib/site-packages/win32com/server/localserver.py
@@ -0,0 +1,49 @@
+# LocalServer .EXE support for Python.
+#
+# This is designed to be used as a _script_ file by pythonw.exe
+#
+# In some cases, you could also use Python.exe, which will create
+# a console window useful for debugging.
+#
+# NOTE: When NOT running in any sort of debugging mode,
+# 'print' statements may fail, as sys.stdout is not valid!!!
+
+#
+# Usage:
+# wpython.exe LocalServer.py clsid [, clsid]
+import sys
+sys.coinit_flags = 2
+import pythoncom
+import win32api
+from win32com.server import factory
+
+usage = """\
+Invalid command line arguments
+
+This program provides LocalServer COM support
+for Python COM objects.
+
+It is typically run automatically by COM, passing as arguments
+The ProgID or CLSID of the Python Server(s) to be hosted
+"""
+
+def serve(clsids):
+ infos = factory.RegisterClassFactories(clsids)
+
+ pythoncom.EnableQuitMessage(win32api.GetCurrentThreadId())
+ pythoncom.CoResumeClassObjects()
+
+ pythoncom.PumpMessages()
+
+ factory.RevokeClassFactories( infos )
+
+ pythoncom.CoUninitialize()
+
+def main():
+ if len(sys.argv)==1:
+ win32api.MessageBox(0, usage, "Python COM Server")
+ sys.exit(1)
+ serve(sys.argv[1:])
+
+if __name__=='__main__':
+ main()
diff --git a/Lib/site-packages/win32com/server/policy.py b/Lib/site-packages/win32com/server/policy.py
new file mode 100644
index 0000000..bb557e4
--- /dev/null
+++ b/Lib/site-packages/win32com/server/policy.py
@@ -0,0 +1,750 @@
+"""Policies
+
+Note that Dispatchers are now implemented in "dispatcher.py", but
+are still documented here.
+
+Policies
+
+ A policy is an object which manages the interaction between a public
+ Python object, and COM . In simple terms, the policy object is the
+ object which is actually called by COM, and it invokes the requested
+ method, fetches/sets the requested property, etc. See the
+ @win32com.server.policy.CreateInstance@ method for a description of
+ how a policy is specified or created.
+
+ Exactly how a policy determines which underlying object method/property
+ is obtained is up to the policy. A few policies are provided, but you
+ can build your own. See each policy class for a description of how it
+ implements its policy.
+
+ There is a policy that allows the object to specify exactly which
+ methods and properties will be exposed. There is also a policy that
+ will dynamically expose all Python methods and properties - even those
+ added after the object has been instantiated.
+
+Dispatchers
+
+ A Dispatcher is a level in front of a Policy. A dispatcher is the
+ thing which actually receives the COM calls, and passes them to the
+ policy object (which in turn somehow does something with the wrapped
+ object).
+
+ It is important to note that a policy does not need to have a dispatcher.
+ A dispatcher has the same interface as a policy, and simply steps in its
+ place, delegating to the real policy. The primary use for a Dispatcher
+ is to support debugging when necessary, but without imposing overheads
+ when not (ie, by not using a dispatcher at all).
+
+ There are a few dispatchers provided - "tracing" dispatchers which simply
+ prints calls and args (including a variation which uses
+ win32api.OutputDebugString), and a "debugger" dispatcher, which can
+ invoke the debugger when necessary.
+
+Error Handling
+
+ It is important to realise that the caller of these interfaces may
+ not be Python. Therefore, general Python exceptions and tracebacks aren't
+ much use.
+
+ In general, there is an Exception class that should be raised, to allow
+ the framework to extract rich COM type error information.
+
+ The general rule is that the **only** exception returned from Python COM
+ Server code should be an Exception instance. Any other Python exception
+ should be considered an implementation bug in the server (if not, it
+ should be handled, and an appropriate Exception instance raised). Any
+ other exception is considered "unexpected", and a dispatcher may take
+ special action (see Dispatchers above)
+
+ Occasionally, the implementation will raise the policy.error error.
+ This usually means there is a problem in the implementation that the
+ Python programmer should fix.
+
+ For example, if policy is asked to wrap an object which it can not
+ support (because, eg, it does not provide _public_methods_ or _dynamic_)
+ then policy.error will be raised, indicating it is a Python programmers
+ problem, rather than a COM error.
+
+"""
+__author__ = "Greg Stein and Mark Hammond"
+
+import win32api
+import winerror
+import sys
+import types
+import pywintypes
+import win32con, pythoncom
+
+#Import a few important constants to speed lookups.
+from pythoncom import \
+ DISPATCH_METHOD, DISPATCH_PROPERTYGET, DISPATCH_PROPERTYPUT, DISPATCH_PROPERTYPUTREF, \
+ DISPID_UNKNOWN, DISPID_VALUE, DISPID_PROPERTYPUT, DISPID_NEWENUM, \
+ DISPID_EVALUATE, DISPID_CONSTRUCTOR, DISPID_DESTRUCTOR, DISPID_COLLECT,DISPID_STARTENUM
+
+S_OK = 0
+
+# Few more globals to speed things.
+IDispatchType = pythoncom.TypeIIDs[pythoncom.IID_IDispatch]
+IUnknownType = pythoncom.TypeIIDs[pythoncom.IID_IUnknown]
+
+from .exception import COMException
+error = __name__ + " error"
+
+regSpec = 'CLSID\\%s\\PythonCOM'
+regPolicy = 'CLSID\\%s\\PythonCOMPolicy'
+regDispatcher = 'CLSID\\%s\\PythonCOMDispatcher'
+regAddnPath = 'CLSID\\%s\\PythonCOMPath'
+
+def CreateInstance(clsid, reqIID):
+ """Create a new instance of the specified IID
+
+ The COM framework **always** calls this function to create a new
+ instance for the specified CLSID. This function looks up the
+ registry for the name of a policy, creates the policy, and asks the
+ policy to create the specified object by calling the _CreateInstance_ method.
+
+ Exactly how the policy creates the instance is up to the policy. See the
+ specific policy documentation for more details.
+ """
+ # First see is sys.path should have something on it.
+ try:
+ addnPaths = win32api.RegQueryValue(win32con.HKEY_CLASSES_ROOT,
+ regAddnPath % clsid).split(';')
+ for newPath in addnPaths:
+ if newPath not in sys.path:
+ sys.path.insert(0, newPath)
+ except win32api.error:
+ pass
+ try:
+ policy = win32api.RegQueryValue(win32con.HKEY_CLASSES_ROOT,
+ regPolicy % clsid)
+ policy = resolve_func(policy)
+ except win32api.error:
+ policy = DefaultPolicy
+
+ try:
+ dispatcher = win32api.RegQueryValue(win32con.HKEY_CLASSES_ROOT,
+ regDispatcher % clsid)
+ if dispatcher: dispatcher = resolve_func(dispatcher)
+ except win32api.error:
+ dispatcher = None
+
+ if dispatcher:
+ retObj = dispatcher(policy, None)
+ else:
+ retObj = policy(None)
+ return retObj._CreateInstance_(clsid, reqIID)
+
+class BasicWrapPolicy:
+ """The base class of policies.
+
+ Normally not used directly (use a child class, instead)
+
+ This policy assumes we are wrapping another object
+ as the COM server. This supports the delegation of the core COM entry points
+ to either the wrapped object, or to a child class.
+
+ This policy supports the following special attributes on the wrapped object
+
+ _query_interface_ -- A handler which can respond to the COM 'QueryInterface' call.
+ _com_interfaces_ -- An optional list of IIDs which the interface will assume are
+ valid for the object.
+ _invoke_ -- A handler which can respond to the COM 'Invoke' call. If this attribute
+ is not provided, then the default policy implementation is used. If this attribute
+ does exist, it is responsible for providing all required functionality - ie, the
+ policy _invoke_ method is not invoked at all (and nor are you able to call it!)
+ _getidsofnames_ -- A handler which can respond to the COM 'GetIDsOfNames' call. If this attribute
+ is not provided, then the default policy implementation is used. If this attribute
+ does exist, it is responsible for providing all required functionality - ie, the
+ policy _getidsofnames_ method is not invoked at all (and nor are you able to call it!)
+
+ IDispatchEx functionality:
+
+ _invokeex_ -- Very similar to _invoke_, except slightly different arguments are used.
+ And the result is just the _real_ result (rather than the (hresult, argErr, realResult)
+ tuple that _invoke_ uses.
+ This is the new, prefered handler (the default _invoke_ handler simply called _invokeex_)
+ _getdispid_ -- Very similar to _getidsofnames_, except slightly different arguments are used,
+ and only 1 property at a time can be fetched (which is all we support in getidsofnames anyway!)
+ This is the new, prefered handler (the default _invoke_ handler simply called _invokeex_)
+ _getnextdispid_- uses self._name_to_dispid_ to enumerate the DISPIDs
+ """
+ def __init__(self, object):
+ """Initialise the policy object
+
+ Params:
+
+ object -- The object to wrap. May be None *iff* @BasicWrapPolicy._CreateInstance_@ will be
+ called immediately after this to setup a brand new object
+ """
+ if object is not None:
+ self._wrap_(object)
+
+ def _CreateInstance_(self, clsid, reqIID):
+ """Creates a new instance of a **wrapped** object
+
+ This method looks up a "@win32com.server.policy.regSpec@" % clsid entry
+ in the registry (using @DefaultPolicy@)
+ """
+ try:
+ classSpec = win32api.RegQueryValue(win32con.HKEY_CLASSES_ROOT,
+ regSpec % clsid)
+ except win32api.error:
+ raise error("The object is not correctly registered - %s key can not be read" % (regSpec % clsid))
+ myob = call_func(classSpec)
+ self._wrap_(myob)
+ try:
+ return pythoncom.WrapObject(self, reqIID)
+ except pythoncom.com_error as xxx_todo_changeme:
+ (hr, desc, exc, arg) = xxx_todo_changeme.args
+ from win32com.util import IIDToInterfaceName
+ desc = "The object '%r' was created, but does not support the " \
+ "interface '%s'(%s): %s" \
+ % (myob, IIDToInterfaceName(reqIID), reqIID, desc)
+ raise pythoncom.com_error(hr, desc, exc, arg)
+
+
+ def _wrap_(self, object):
+ """Wraps up the specified object.
+
+ This function keeps a reference to the passed
+ object, and may interogate it to determine how to respond to COM requests, etc.
+ """
+ # We "clobber" certain of our own methods with ones
+ # provided by the wrapped object, iff they exist.
+ self._name_to_dispid_ = { }
+ ob = self._obj_ = object
+ if hasattr(ob, '_query_interface_'):
+ self._query_interface_ = ob._query_interface_
+
+ if hasattr(ob, '_invoke_'):
+ self._invoke_ = ob._invoke_
+
+ if hasattr(ob, '_invokeex_'):
+ self._invokeex_ = ob._invokeex_
+
+ if hasattr(ob, '_getidsofnames_'):
+ self._getidsofnames_ = ob._getidsofnames_
+
+ if hasattr(ob, '_getdispid_'):
+ self._getdispid_ = ob._getdispid_
+
+ # Allow for override of certain special attributes.
+ if hasattr(ob, '_com_interfaces_'):
+ self._com_interfaces_ = []
+ # Allow interfaces to be specified by name.
+ for i in ob._com_interfaces_:
+ if type(i) != pywintypes.IIDType:
+ # Prolly a string!
+ if i[0] != "{":
+ i = pythoncom.InterfaceNames[i]
+ else:
+ i = pythoncom.MakeIID(i)
+ self._com_interfaces_.append(i)
+ else:
+ self._com_interfaces_ = [ ]
+
+ # "QueryInterface" handling.
+ def _QueryInterface_(self, iid):
+ """The main COM entry-point for QueryInterface.
+
+ This checks the _com_interfaces_ attribute and if the interface is not specified
+ there, it calls the derived helper _query_interface_
+ """
+ if iid in self._com_interfaces_:
+ return 1
+ return self._query_interface_(iid)
+
+ def _query_interface_(self, iid):
+ """Called if the object does not provide the requested interface in _com_interfaces,
+ and does not provide a _query_interface_ handler.
+
+ Returns a result to the COM framework indicating the interface is not supported.
+ """
+ return 0
+
+ # "Invoke" handling.
+ def _Invoke_(self, dispid, lcid, wFlags, args):
+ """The main COM entry-point for Invoke.
+
+ This calls the _invoke_ helper.
+ """
+ #Translate a possible string dispid to real dispid.
+ if type(dispid) == type(""):
+ try:
+ dispid = self._name_to_dispid_[dispid.lower()]
+ except KeyError:
+ raise COMException(scode = winerror.DISP_E_MEMBERNOTFOUND, desc="Member not found")
+ return self._invoke_(dispid, lcid, wFlags, args)
+
+ def _invoke_(self, dispid, lcid, wFlags, args):
+ # Delegates to the _invokeex_ implementation. This allows
+ # a custom policy to define _invokeex_, and automatically get _invoke_ too.
+ return S_OK, -1, self._invokeex_(dispid, lcid, wFlags, args, None, None)
+
+ # "GetIDsOfNames" handling.
+ def _GetIDsOfNames_(self, names, lcid):
+ """The main COM entry-point for GetIDsOfNames.
+
+ This checks the validity of the arguments, and calls the _getidsofnames_ helper.
+ """
+ if len(names) > 1:
+ raise COMException(scode = winerror.DISP_E_INVALID, desc="Cannot support member argument names")
+ return self._getidsofnames_(names, lcid)
+
+ def _getidsofnames_(self, names, lcid):
+ ### note: lcid is being ignored...
+ return (self._getdispid_(names[0], 0), )
+
+ # IDispatchEx support for policies. Most of the IDispathEx functionality
+ # by default will raise E_NOTIMPL. Thus it is not necessary for derived
+ # policies to explicitely implement all this functionality just to not implement it!
+
+ def _GetDispID_(self, name, fdex):
+ return self._getdispid_(name, fdex)
+
+ def _getdispid_(self, name, fdex):
+ try:
+ ### TODO - look at the fdex flags!!!
+ return self._name_to_dispid_[name.lower()]
+ except KeyError:
+ raise COMException(scode = winerror.DISP_E_UNKNOWNNAME)
+
+ # "InvokeEx" handling.
+ def _InvokeEx_(self, dispid, lcid, wFlags, args, kwargs, serviceProvider):
+ """The main COM entry-point for InvokeEx.
+
+ This calls the _invokeex_ helper.
+ """
+ #Translate a possible string dispid to real dispid.
+ if type(dispid) == type(""):
+ try:
+ dispid = self._name_to_dispid_[dispid.lower()]
+ except KeyError:
+ raise COMException(scode = winerror.DISP_E_MEMBERNOTFOUND, desc="Member not found")
+ return self._invokeex_(dispid, lcid, wFlags, args, kwargs, serviceProvider)
+
+ def _invokeex_(self, dispid, lcid, wFlags, args, kwargs, serviceProvider):
+ """A stub for _invokeex_ - should never be called.
+
+ Simply raises an exception.
+ """
+ # Base classes should override this method (and not call the base)
+ raise error("This class does not provide _invokeex_ semantics")
+
+ def _DeleteMemberByName_(self, name, fdex):
+ return self._deletememberbyname_(name, fdex)
+ def _deletememberbyname_(self, name, fdex):
+ raise COMException(scode = winerror.E_NOTIMPL)
+
+ def _DeleteMemberByDispID_(self, id):
+ return self._deletememberbydispid(id)
+ def _deletememberbydispid_(self, id):
+ raise COMException(scode = winerror.E_NOTIMPL)
+
+ def _GetMemberProperties_(self, id, fdex):
+ return self._getmemberproperties_(id, fdex)
+ def _getmemberproperties_(self, id, fdex):
+ raise COMException(scode = winerror.E_NOTIMPL)
+
+ def _GetMemberName_(self, dispid):
+ return self._getmembername_(dispid)
+ def _getmembername_(self, dispid):
+ raise COMException(scode = winerror.E_NOTIMPL)
+
+ def _GetNextDispID_(self, fdex, dispid):
+ return self._getnextdispid_(fdex, dispid)
+ def _getnextdispid_(self, fdex, dispid):
+ ids = list(self._name_to_dispid_.values())
+ ids.sort()
+ if DISPID_STARTENUM in ids: ids.remove(DISPID_STARTENUM)
+ if dispid==DISPID_STARTENUM:
+ return ids[0]
+ else:
+ try:
+ return ids[ids.index(dispid)+1]
+ except ValueError: # dispid not in list?
+ raise COMException(scode = winerror.E_UNEXPECTED)
+ except IndexError: # No more items
+ raise COMException(scode = winerror.S_FALSE)
+
+ def _GetNameSpaceParent_(self):
+ return self._getnamespaceparent()
+ def _getnamespaceparent_(self):
+ raise COMException(scode = winerror.E_NOTIMPL)
+
+
+class MappedWrapPolicy(BasicWrapPolicy):
+ """Wraps an object using maps to do its magic
+
+ This policy wraps up a Python object, using a number of maps
+ which translate from a Dispatch ID and flags, into an object to call/getattr, etc.
+
+ It is the responsibility of derived classes to determine exactly how the
+ maps are filled (ie, the derived classes determine the map filling policy.
+
+ This policy supports the following special attributes on the wrapped object
+
+ _dispid_to_func_/_dispid_to_get_/_dispid_to_put_ -- These are dictionaries
+ (keyed by integer dispid, values are string attribute names) which the COM
+ implementation uses when it is processing COM requests. Note that the implementation
+ uses this dictionary for its own purposes - not a copy - which means the contents of
+ these dictionaries will change as the object is used.
+
+ """
+ def _wrap_(self, object):
+ BasicWrapPolicy._wrap_(self, object)
+ ob = self._obj_
+ if hasattr(ob, '_dispid_to_func_'):
+ self._dispid_to_func_ = ob._dispid_to_func_
+ else:
+ self._dispid_to_func_ = { }
+ if hasattr(ob, '_dispid_to_get_'):
+ self._dispid_to_get_ = ob._dispid_to_get_
+ else:
+ self._dispid_to_get_ = { }
+ if hasattr(ob, '_dispid_to_put_'):
+ self._dispid_to_put_ = ob._dispid_to_put_
+ else:
+ self._dispid_to_put_ = { }
+
+ def _getmembername_(self, dispid):
+ if dispid in self._dispid_to_func_:
+ return self._dispid_to_func_[dispid]
+ elif dispid in self._dispid_to_get_:
+ return self._dispid_to_get_[dispid]
+ elif dispid in self._dispid_to_put_:
+ return self._dispid_to_put_[dispid]
+ else:
+ raise COMException(scode = winerror.DISP_E_MEMBERNOTFOUND)
+
+class DesignatedWrapPolicy(MappedWrapPolicy):
+ """A policy which uses a mapping to link functions and dispid
+
+ A MappedWrappedPolicy which allows the wrapped object to specify, via certain
+ special named attributes, exactly which methods and properties are exposed.
+
+ All a wrapped object need do is provide the special attributes, and the policy
+ will handle everything else.
+
+ Attributes:
+
+ _public_methods_ -- Required, unless a typelib GUID is given -- A list
+ of strings, which must be the names of methods the object
+ provides. These methods will be exposed and callable
+ from other COM hosts.
+ _public_attrs_ A list of strings, which must be the names of attributes on the object.
+ These attributes will be exposed and readable and possibly writeable from other COM hosts.
+ _readonly_attrs_ -- A list of strings, which must also appear in _public_attrs. These
+ attributes will be readable, but not writable, by other COM hosts.
+ _value_ -- A method that will be called if the COM host requests the "default" method
+ (ie, calls Invoke with dispid==DISPID_VALUE)
+ _NewEnum -- A method that will be called if the COM host requests an enumerator on the
+ object (ie, calls Invoke with dispid==DISPID_NEWENUM.)
+ It is the responsibility of the method to ensure the returned
+ object conforms to the required Enum interface.
+
+ _typelib_guid_ -- The GUID of the typelibrary with interface definitions we use.
+ _typelib_version_ -- A tuple of (major, minor) with a default of 1,1
+ _typelib_lcid_ -- The LCID of the typelib, default = LOCALE_USER_DEFAULT
+
+ _Evaluate -- Dunno what this means, except the host has called Invoke with dispid==DISPID_EVALUATE!
+ See the COM documentation for details.
+ """
+ def _wrap_(self, ob):
+ # If we have nominated universal interfaces to support, load them now
+ tlb_guid = getattr(ob, '_typelib_guid_', None)
+ if tlb_guid is not None:
+ tlb_major, tlb_minor = getattr(ob, '_typelib_version_', (1,0))
+ tlb_lcid = getattr(ob, '_typelib_lcid_', 0)
+ from win32com import universal
+ # XXX - what if the user wants to implement interfaces from multiple
+ # typelibs?
+ # Filter out all 'normal' IIDs (ie, IID objects and strings starting with {
+ interfaces = [i for i in getattr(ob, '_com_interfaces_', [])
+ if type(i) != pywintypes.IIDType and not i.startswith("{")]
+ universal_data = universal.RegisterInterfaces(tlb_guid, tlb_lcid,
+ tlb_major, tlb_minor, interfaces)
+ else:
+ universal_data = []
+ MappedWrapPolicy._wrap_(self, ob)
+ if not hasattr(ob, '_public_methods_') and not hasattr(ob, "_typelib_guid_"):
+ raise error("Object does not support DesignatedWrapPolicy, as it does not have either _public_methods_ or _typelib_guid_ attributes.")
+
+ # Copy existing _dispid_to_func_ entries to _name_to_dispid_
+ for dispid, name in self._dispid_to_func_.items():
+ self._name_to_dispid_[name.lower()]=dispid
+ for dispid, name in self._dispid_to_get_.items():
+ self._name_to_dispid_[name.lower()]=dispid
+ for dispid, name in self._dispid_to_put_.items():
+ self._name_to_dispid_[name.lower()]=dispid
+
+ # Patch up the universal stuff.
+ for dispid, invkind, name in universal_data:
+ self._name_to_dispid_[name.lower()]=dispid
+ if invkind == DISPATCH_METHOD:
+ self._dispid_to_func_[dispid] = name
+ elif invkind in (DISPATCH_PROPERTYPUT, DISPATCH_PROPERTYPUTREF):
+ self._dispid_to_put_[dispid] = name
+ elif invkind == DISPATCH_PROPERTYGET:
+ self._dispid_to_get_[dispid] = name
+ else:
+ raise ValueError("unexpected invkind: %d (%s)" % (invkind,name))
+
+ # look for reserved methods
+ if hasattr(ob, '_value_'):
+ self._dispid_to_get_[DISPID_VALUE] = '_value_'
+ self._dispid_to_put_[DISPID_PROPERTYPUT] = '_value_'
+ if hasattr(ob, '_NewEnum'):
+ self._name_to_dispid_['_newenum'] = DISPID_NEWENUM
+ self._dispid_to_func_[DISPID_NEWENUM] = '_NewEnum'
+ if hasattr(ob, '_Evaluate'):
+ self._name_to_dispid_['_evaluate'] = DISPID_EVALUATE
+ self._dispid_to_func_[DISPID_EVALUATE] = '_Evaluate'
+
+ next_dispid = self._allocnextdispid(999)
+ # note: funcs have precedence over attrs (install attrs first)
+ if hasattr(ob, '_public_attrs_'):
+ if hasattr(ob, '_readonly_attrs_'):
+ readonly = ob._readonly_attrs_
+ else:
+ readonly = [ ]
+ for name in ob._public_attrs_:
+ dispid = self._name_to_dispid_.get(name.lower())
+ if dispid is None:
+ dispid = next_dispid
+ self._name_to_dispid_[name.lower()] = dispid
+ next_dispid = self._allocnextdispid(next_dispid)
+ self._dispid_to_get_[dispid] = name
+ if name not in readonly:
+ self._dispid_to_put_[dispid] = name
+ for name in getattr(ob, "_public_methods_", []):
+ dispid = self._name_to_dispid_.get(name.lower())
+ if dispid is None:
+ dispid = next_dispid
+ self._name_to_dispid_[name.lower()] = dispid
+ next_dispid = self._allocnextdispid(next_dispid)
+ self._dispid_to_func_[dispid] = name
+ self._typeinfos_ = None # load these on demand.
+
+ def _build_typeinfos_(self):
+ # Can only ever be one for now.
+ tlb_guid = getattr(self._obj_, '_typelib_guid_', None)
+ if tlb_guid is None:
+ return []
+ tlb_major, tlb_minor = getattr(self._obj_, '_typelib_version_', (1,0))
+ tlb = pythoncom.LoadRegTypeLib(tlb_guid, tlb_major, tlb_minor)
+ typecomp = tlb.GetTypeComp()
+ # Not 100% sure what semantics we should use for the default interface.
+ # Look for the first name in _com_interfaces_ that exists in the typelib.
+ for iname in self._obj_._com_interfaces_:
+ try:
+ type_info, type_comp = typecomp.BindType(iname)
+ if type_info is not None:
+ return [type_info]
+ except pythoncom.com_error:
+ pass
+ return []
+
+ def _GetTypeInfoCount_(self):
+ if self._typeinfos_ is None:
+ self._typeinfos_ = self._build_typeinfos_()
+ return len(self._typeinfos_)
+
+ def _GetTypeInfo_(self, index, lcid):
+ if self._typeinfos_ is None:
+ self._typeinfos_ = self._build_typeinfos_()
+ if index < 0 or index >= len(self._typeinfos_):
+ raise COMException(scode=winerror.DISP_E_BADINDEX)
+ return 0, self._typeinfos_[index]
+
+ def _allocnextdispid(self, last_dispid):
+ while 1:
+ last_dispid = last_dispid + 1
+ if last_dispid not in self._dispid_to_func_ and \
+ last_dispid not in self._dispid_to_get_ and \
+ last_dispid not in self._dispid_to_put_:
+ return last_dispid
+
+ def _invokeex_(self, dispid, lcid, wFlags, args, kwArgs, serviceProvider):
+ ### note: lcid is being ignored...
+
+ if wFlags & DISPATCH_METHOD:
+ try:
+ funcname = self._dispid_to_func_[dispid]
+ except KeyError:
+ if not wFlags & DISPATCH_PROPERTYGET:
+ raise COMException(scode=winerror.DISP_E_MEMBERNOTFOUND) # not found
+ else:
+ try:
+ func = getattr(self._obj_, funcname)
+ except AttributeError:
+ # May have a dispid, but that doesnt mean we have the function!
+ raise COMException(scode=winerror.DISP_E_MEMBERNOTFOUND)
+ # Should check callable here
+ try:
+ return func(*args)
+ except TypeError as v:
+ # Particularly nasty is "wrong number of args" type error
+ # This helps you see what 'func' and 'args' actually is
+ if str(v).find("arguments")>=0:
+ print("** TypeError %s calling function %r(%r)" % (v, func, args))
+ raise
+
+ if wFlags & DISPATCH_PROPERTYGET:
+ try:
+ name = self._dispid_to_get_[dispid]
+ except KeyError:
+ raise COMException(scode=winerror.DISP_E_MEMBERNOTFOUND) # not found
+ retob = getattr(self._obj_, name)
+ if type(retob)==types.MethodType: # a method as a property - call it.
+ retob = retob(*args)
+ return retob
+
+ if wFlags & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF): ### correct?
+ try:
+ name = self._dispid_to_put_[dispid]
+ except KeyError:
+ raise COMException(scode=winerror.DISP_E_MEMBERNOTFOUND) # read-only
+ # If we have a method of that name (ie, a property get function), and
+ # we have an equiv. property set function, use that instead.
+ if type(getattr(self._obj_, name, None)) == types.MethodType and \
+ type(getattr(self._obj_, "Set" + name, None)) == types.MethodType:
+ fn = getattr(self._obj_, "Set" + name)
+ fn( *args )
+ else:
+ # just set the attribute
+ setattr(self._obj_, name, args[0])
+ return
+
+ raise COMException(scode=winerror.E_INVALIDARG, desc="invalid wFlags")
+
+class EventHandlerPolicy(DesignatedWrapPolicy):
+ """The default policy used by event handlers in the win32com.client package.
+
+ In addition to the base policy, this provides argument conversion semantics for
+ params
+ * dispatch params are converted to dispatch objects.
+ * Unicode objects are converted to strings (1.5.2 and earlier)
+
+ NOTE: Later, we may allow the object to override this process??
+ """
+ def _transform_args_(self, args, kwArgs, dispid, lcid, wFlags, serviceProvider):
+ ret = []
+ for arg in args:
+ arg_type = type(arg)
+ if arg_type == IDispatchType:
+ import win32com.client
+ arg = win32com.client.Dispatch(arg)
+ elif arg_type == IUnknownType:
+ try:
+ import win32com.client
+ arg = win32com.client.Dispatch(arg.QueryInterface(pythoncom.IID_IDispatch))
+ except pythoncom.error:
+ pass # Keep it as IUnknown
+ ret.append(arg)
+ return tuple(ret), kwArgs
+ def _invokeex_(self, dispid, lcid, wFlags, args, kwArgs, serviceProvider):
+ # transform the args.
+ args, kwArgs = self._transform_args_(args, kwArgs, dispid, lcid, wFlags, serviceProvider)
+ return DesignatedWrapPolicy._invokeex_( self, dispid, lcid, wFlags, args, kwArgs, serviceProvider)
+
+class DynamicPolicy(BasicWrapPolicy):
+ """A policy which dynamically (ie, at run-time) determines public interfaces.
+
+ A dynamic policy is used to dynamically dispatch methods and properties to the
+ wrapped object. The list of objects and properties does not need to be known in
+ advance, and methods or properties added to the wrapped object after construction
+ are also handled.
+
+ The wrapped object must provide the following attributes:
+
+ _dynamic_ -- A method that will be called whenever an invoke on the object
+ is called. The method is called with the name of the underlying method/property
+ (ie, the mapping of dispid to/from name has been resolved.) This name property
+ may also be '_value_' to indicate the default, and '_NewEnum' to indicate a new
+ enumerator is requested.
+
+ """
+ def _wrap_(self, object):
+ BasicWrapPolicy._wrap_(self, object)
+ if not hasattr(self._obj_, '_dynamic_'):
+ raise error("Object does not support Dynamic COM Policy")
+ self._next_dynamic_ = self._min_dynamic_ = 1000
+ self._dyn_dispid_to_name_ = {DISPID_VALUE:'_value_', DISPID_NEWENUM:'_NewEnum' }
+
+ def _getdispid_(self, name, fdex):
+ # TODO - Look at fdex flags.
+ lname = name.lower()
+ try:
+ return self._name_to_dispid_[lname]
+ except KeyError:
+ dispid = self._next_dynamic_ = self._next_dynamic_ + 1
+ self._name_to_dispid_[lname] = dispid
+ self._dyn_dispid_to_name_[dispid] = name # Keep case in this map...
+ return dispid
+
+ def _invoke_(self, dispid, lcid, wFlags, args):
+ return S_OK, -1, self._invokeex_(dispid, lcid, wFlags, args, None, None)
+
+ def _invokeex_(self, dispid, lcid, wFlags, args, kwargs, serviceProvider):
+ ### note: lcid is being ignored...
+ ### note: kwargs is being ignored...
+ ### note: serviceProvider is being ignored...
+ ### there might be assigned DISPID values to properties, too...
+ try:
+ name = self._dyn_dispid_to_name_[dispid]
+ except KeyError:
+ raise COMException(scode = winerror.DISP_E_MEMBERNOTFOUND, desc="Member not found")
+ return self._obj_._dynamic_(name, lcid, wFlags, args)
+
+
+DefaultPolicy = DesignatedWrapPolicy
+
+def resolve_func(spec):
+ """Resolve a function by name
+
+ Given a function specified by 'module.function', return a callable object
+ (ie, the function itself)
+ """
+ try:
+ idx = spec.rindex(".")
+ mname = spec[:idx]
+ fname = spec[idx+1:]
+ # Dont attempt to optimize by looking in sys.modules,
+ # as another thread may also be performing the import - this
+ # way we take advantage of the built-in import lock.
+ module = _import_module(mname)
+ return getattr(module, fname)
+ except ValueError: # No "." in name - assume in this module
+ return globals()[spec]
+
+def call_func(spec, *args):
+ """Call a function specified by name.
+
+ Call a function specified by 'module.function' and return the result.
+ """
+
+ return resolve_func(spec)(*args)
+
+def _import_module(mname):
+ """Import a module just like the 'import' statement.
+
+ Having this function is much nicer for importing arbitrary modules than
+ using the 'exec' keyword. It is more efficient and obvious to the reader.
+ """
+ __import__(mname)
+ # Eeek - result of _import_ is "win32com" - not "win32com.a.b.c"
+ # Get the full module from sys.modules
+ return sys.modules[mname]
+
+#######
+#
+# Temporary hacks until all old code moves.
+#
+# These have been moved to a new source file, but some code may
+# still reference them here. These will end up being removed.
+try:
+ from .dispatcher import DispatcherTrace, DispatcherWin32trace
+except ImportError: # Quite likely a frozen executable that doesnt need dispatchers
+ pass
diff --git a/Lib/site-packages/win32com/server/register.py b/Lib/site-packages/win32com/server/register.py
new file mode 100644
index 0000000..a6a9f14
--- /dev/null
+++ b/Lib/site-packages/win32com/server/register.py
@@ -0,0 +1,617 @@
+"""Utilities for registering objects.
+
+This module contains utility functions to register Python objects as
+valid COM Servers. The RegisterServer function provides all information
+necessary to allow the COM framework to respond to a request for a COM object,
+construct the necessary Python object, and dispatch COM events.
+
+"""
+import sys
+import win32api
+import win32con
+import pythoncom
+import winerror
+import os
+
+CATID_PythonCOMServer = "{B3EF80D0-68E2-11D0-A689-00C04FD658FF}"
+
+def _set_subkeys(keyName, valueDict, base=win32con.HKEY_CLASSES_ROOT):
+ hkey = win32api.RegCreateKey(base, keyName)
+ try:
+ for key, value in valueDict.items():
+ win32api.RegSetValueEx(hkey, key, None, win32con.REG_SZ, value)
+ finally:
+ win32api.RegCloseKey(hkey)
+
+def _set_string(path, value, base=win32con.HKEY_CLASSES_ROOT):
+ "Set a string value in the registry."
+
+ win32api.RegSetValue(base,
+ path,
+ win32con.REG_SZ,
+ value)
+
+def _get_string(path, base=win32con.HKEY_CLASSES_ROOT):
+ "Get a string value from the registry."
+
+ try:
+ return win32api.RegQueryValue(base, path)
+ except win32api.error:
+ return None
+
+def _remove_key(path, base=win32con.HKEY_CLASSES_ROOT):
+ "Remove a string from the registry."
+
+ try:
+ win32api.RegDeleteKey(base, path)
+ except win32api.error as xxx_todo_changeme1:
+ (code, fn, msg) = xxx_todo_changeme1.args
+ if code != winerror.ERROR_FILE_NOT_FOUND:
+ raise win32api.error(code, fn, msg)
+
+def recurse_delete_key(path, base=win32con.HKEY_CLASSES_ROOT):
+ """Recursively delete registry keys.
+
+ This is needed since you can't blast a key when subkeys exist.
+ """
+ try:
+ h = win32api.RegOpenKey(base, path)
+ except win32api.error as xxx_todo_changeme2:
+ (code, fn, msg) = xxx_todo_changeme2.args
+ if code != winerror.ERROR_FILE_NOT_FOUND:
+ raise win32api.error(code, fn, msg)
+ else:
+ # parent key found and opened successfully. do some work, making sure
+ # to always close the thing (error or no).
+ try:
+ # remove all of the subkeys
+ while 1:
+ try:
+ subkeyname = win32api.RegEnumKey(h, 0)
+ except win32api.error as xxx_todo_changeme:
+ (code, fn, msg) = xxx_todo_changeme.args
+ if code != winerror.ERROR_NO_MORE_ITEMS:
+ raise win32api.error(code, fn, msg)
+ break
+ recurse_delete_key(path + '\\' + subkeyname, base)
+
+ # remove the parent key
+ _remove_key(path, base)
+ finally:
+ win32api.RegCloseKey(h)
+
+def _cat_registrar():
+ return pythoncom.CoCreateInstance(
+ pythoncom.CLSID_StdComponentCategoriesMgr,
+ None,
+ pythoncom.CLSCTX_INPROC_SERVER,
+ pythoncom.IID_ICatRegister
+ )
+
+def _find_localserver_exe(mustfind):
+ if not sys.platform.startswith("win32"):
+ return sys.executable
+ if pythoncom.__file__.find("_d") < 0:
+ exeBaseName = "pythonw.exe"
+ else:
+ exeBaseName = "pythonw_d.exe"
+ # First see if in the same directory as this .EXE
+ exeName = os.path.join( os.path.split(sys.executable)[0], exeBaseName )
+ if not os.path.exists(exeName):
+ # See if in our sys.prefix directory
+ exeName = os.path.join( sys.prefix, exeBaseName )
+ if not os.path.exists(exeName):
+ # See if in our sys.prefix/pcbuild directory (for developers)
+ if "64 bit" in sys.version:
+ exeName = os.path.join( sys.prefix, "PCbuild", "amd64", exeBaseName )
+ else:
+ exeName = os.path.join( sys.prefix, "PCbuild", exeBaseName )
+ if not os.path.exists(exeName):
+ # See if the registry has some info.
+ try:
+ key = "SOFTWARE\\Python\\PythonCore\\%s\\InstallPath" % sys.winver
+ path = win32api.RegQueryValue( win32con.HKEY_LOCAL_MACHINE, key )
+ exeName = os.path.join( path, exeBaseName )
+ except (AttributeError,win32api.error):
+ pass
+ if not os.path.exists(exeName):
+ if mustfind:
+ raise RuntimeError("Can not locate the program '%s'" % exeBaseName)
+ return None
+ return exeName
+
+def _find_localserver_module():
+ import win32com.server
+ path = win32com.server.__path__[0]
+ baseName = "localserver"
+ pyfile = os.path.join(path, baseName + ".py")
+ try:
+ os.stat(pyfile)
+ except os.error:
+ # See if we have a compiled extension
+ if __debug__:
+ ext = ".pyc"
+ else:
+ ext = ".pyo"
+ pyfile = os.path.join(path, baseName + ext)
+ try:
+ os.stat(pyfile)
+ except os.error:
+ raise RuntimeError("Can not locate the Python module 'win32com.server.%s'" % baseName)
+ return pyfile
+
+def RegisterServer(clsid,
+ pythonInstString=None,
+ desc=None,
+ progID=None, verProgID=None,
+ defIcon=None,
+ threadingModel="both",
+ policy=None,
+ catids=[], other={},
+ addPyComCat=None,
+ dispatcher = None,
+ clsctx = None,
+ addnPath = None,
+ ):
+ """Registers a Python object as a COM Server. This enters almost all necessary
+ information in the system registry, allowing COM to use the object.
+
+ clsid -- The (unique) CLSID of the server.
+ pythonInstString -- A string holding the instance name that will be created
+ whenever COM requests a new object.
+ desc -- The description of the COM object.
+ progID -- The user name of this object (eg, Word.Document)
+ verProgId -- The user name of this version's implementation (eg Word.6.Document)
+ defIcon -- The default icon for the object.
+ threadingModel -- The threading model this object supports.
+ policy -- The policy to use when creating this object.
+ catids -- A list of category ID's this object belongs in.
+ other -- A dictionary of extra items to be registered.
+ addPyComCat -- A flag indicating if the object should be added to the list
+ of Python servers installed on the machine. If None (the default)
+ then it will be registered when running from python source, but
+ not registered if running in a frozen environment.
+ dispatcher -- The dispatcher to use when creating this object.
+ clsctx -- One of the CLSCTX_* constants.
+ addnPath -- An additional path the COM framework will add to sys.path
+ before attempting to create the object.
+ """
+
+
+ ### backwards-compat check
+ ### Certain policies do not require a "class name", just the policy itself.
+ if not pythonInstString and not policy:
+ raise TypeError('You must specify either the Python Class or Python Policy which implement the COM object.')
+
+ keyNameRoot = "CLSID\\%s" % str(clsid)
+ _set_string(keyNameRoot, desc)
+
+ # Also register as an "Application" so DCOM etc all see us.
+ _set_string("AppID\\%s" % clsid, progID)
+ # Depending on contexts requested, register the specified server type.
+ # Set default clsctx.
+ if not clsctx:
+ clsctx = pythoncom.CLSCTX_INPROC_SERVER | pythoncom.CLSCTX_LOCAL_SERVER
+ # And if we are frozen, ignore the ones that don't make sense in this
+ # context.
+ if pythoncom.frozen:
+ assert sys.frozen, "pythoncom is frozen, but sys.frozen is not set - don't know the context!"
+ if sys.frozen == "dll":
+ clsctx = clsctx & pythoncom.CLSCTX_INPROC_SERVER
+ else:
+ clsctx = clsctx & pythoncom.CLSCTX_LOCAL_SERVER
+ # Now setup based on the clsctx left over.
+ if clsctx & pythoncom.CLSCTX_INPROC_SERVER:
+ # get the module to use for registration.
+ # nod to Gordon's installer - if sys.frozen and sys.frozendllhandle
+ # exist, then we are being registered via a DLL - use this DLL as the
+ # file name.
+ if pythoncom.frozen:
+ if hasattr(sys, "frozendllhandle"):
+ dllName = win32api.GetModuleFileName(sys.frozendllhandle)
+ else:
+ raise RuntimeError("We appear to have a frozen DLL, but I don't know the DLL to use")
+ else:
+ # Normal case - running from .py file, so register pythoncom's DLL.
+ # Although now we prefer a 'loader' DLL if it exists to avoid some
+ # manifest issues (the 'loader' DLL has a manifest, but pythoncom does not)
+ pythoncom_dir = os.path.dirname(pythoncom.__file__)
+ if pythoncom.__file__.find("_d") < 0:
+ suffix = ""
+ else:
+ suffix = "_d"
+ loadername = "pythoncomloader%d%d%s.dll" % (sys.version_info[0], sys.version_info[1], suffix)
+ if os.path.isfile(os.path.join(pythoncom_dir, loadername)):
+ dllName = loadername
+ else:
+ # just use pythoncom.
+ dllName = os.path.basename(pythoncom.__file__)
+
+ _set_subkeys(keyNameRoot + "\\InprocServer32",
+ { None : dllName,
+ "ThreadingModel" : threadingModel,
+ })
+ else: # Remove any old InProcServer32 registrations
+ _remove_key(keyNameRoot + "\\InprocServer32")
+
+ if clsctx & pythoncom.CLSCTX_LOCAL_SERVER:
+ if pythoncom.frozen:
+ # If we are frozen, we write "{exe} /Automate", just
+ # like "normal" .EXEs do
+ exeName = win32api.GetShortPathName(sys.executable)
+ command = '%s /Automate' % (exeName,)
+ else:
+ # Running from .py sources - we need to write
+ # 'python.exe win32com\server\localserver.py {clsid}"
+ exeName = _find_localserver_exe(1)
+ exeName = win32api.GetShortPathName(exeName)
+ pyfile = _find_localserver_module()
+ command = '%s "%s" %s' % (exeName, pyfile, str(clsid))
+ _set_string(keyNameRoot + '\\LocalServer32', command)
+ else: # Remove any old LocalServer32 registrations
+ _remove_key(keyNameRoot + "\\LocalServer32")
+
+ if pythonInstString:
+ _set_string(keyNameRoot + '\\PythonCOM', pythonInstString)
+ else:
+ _remove_key(keyNameRoot + '\\PythonCOM')
+ if policy:
+ _set_string(keyNameRoot + '\\PythonCOMPolicy', policy)
+ else:
+ _remove_key(keyNameRoot + '\\PythonCOMPolicy')
+
+ if dispatcher:
+ _set_string(keyNameRoot + '\\PythonCOMDispatcher', dispatcher)
+ else:
+ _remove_key(keyNameRoot + '\\PythonCOMDispatcher')
+
+ if defIcon:
+ _set_string(keyNameRoot + '\\DefaultIcon', defIcon)
+ else:
+ _remove_key(keyNameRoot + '\\DefaultIcon')
+
+ if addnPath:
+ _set_string(keyNameRoot + "\\PythonCOMPath", addnPath)
+ else:
+ _remove_key(keyNameRoot + "\\PythonCOMPath")
+
+ if addPyComCat is None:
+ addPyComCat = pythoncom.frozen == 0
+ if addPyComCat:
+ catids = catids + [ CATID_PythonCOMServer ]
+
+ # Set up the implemented categories
+ if catids:
+ regCat = _cat_registrar()
+ regCat.RegisterClassImplCategories(clsid, catids)
+
+ # set up any other reg values they might have
+ if other:
+ for key, value in other.items():
+ _set_string(keyNameRoot + '\\' + key, value)
+
+ if progID:
+ # set the progID as the most specific that was given to us
+ if verProgID:
+ _set_string(keyNameRoot + '\\ProgID', verProgID)
+ else:
+ _set_string(keyNameRoot + '\\ProgID', progID)
+
+ # Set up the root entries - version independent.
+ if desc:
+ _set_string(progID, desc)
+ _set_string(progID + '\\CLSID', str(clsid))
+
+ # Set up the root entries - version dependent.
+ if verProgID:
+ # point from independent to the current version
+ _set_string(progID + '\\CurVer', verProgID)
+
+ # point to the version-independent one
+ _set_string(keyNameRoot + '\\VersionIndependentProgID', progID)
+
+ # set up the versioned progID
+ if desc:
+ _set_string(verProgID, desc)
+ _set_string(verProgID + '\\CLSID', str(clsid))
+
+def GetUnregisterServerKeys(clsid, progID=None, verProgID=None, customKeys = None):
+ """Given a server, return a list of of ("key", root), which are keys recursively
+ and uncondtionally deleted at unregister or uninstall time.
+ """
+ # remove the main CLSID registration
+ ret = [("CLSID\\%s" % str(clsid), win32con.HKEY_CLASSES_ROOT)]
+ # remove the versioned ProgID registration
+ if verProgID:
+ ret.append((verProgID, win32con.HKEY_CLASSES_ROOT))
+ # blow away the independent ProgID. we can't leave it since we just
+ # torched the class.
+ ### could potentially check the CLSID... ?
+ if progID:
+ ret.append((progID, win32con.HKEY_CLASSES_ROOT))
+ # The DCOM config tool may write settings to the AppID key for our CLSID
+ ret.append( ("AppID\\%s" % str(clsid), win32con.HKEY_CLASSES_ROOT) )
+ # Any custom keys?
+ if customKeys:
+ ret = ret + customKeys
+
+ return ret
+
+
+def UnregisterServer(clsid, progID=None, verProgID=None, customKeys = None):
+ """Unregisters a Python COM server."""
+
+ for args in GetUnregisterServerKeys(clsid, progID, verProgID, customKeys ):
+ recurse_delete_key(*args)
+
+ ### it might be nice at some point to "roll back" the independent ProgID
+ ### to an earlier version if one exists, and just blowing away the
+ ### specified version of the ProgID (and its corresponding CLSID)
+ ### another time, though...
+
+ ### NOTE: ATL simply blows away the above three keys without the
+ ### potential checks that I describe. Assuming that defines the
+ ### "standard" then we have no additional changes necessary.
+
+def GetRegisteredServerOption(clsid, optionName):
+ """Given a CLSID for a server and option name, return the option value
+ """
+ keyNameRoot = "CLSID\\%s\\%s" % (str(clsid), str(optionName))
+ return _get_string(keyNameRoot)
+
+
+def _get(ob, attr, default=None):
+ try:
+ return getattr(ob, attr)
+ except AttributeError:
+ pass
+ # look down sub-classes
+ try:
+ bases = ob.__bases__
+ except AttributeError:
+ # ob is not a class - no probs.
+ return default
+ for base in bases:
+ val = _get(base, attr, None)
+ if val is not None:
+ return val
+ return default
+
+def RegisterClasses(*classes, **flags):
+ quiet = 'quiet' in flags and flags['quiet']
+ debugging = 'debug' in flags and flags['debug']
+ for cls in classes:
+ clsid = cls._reg_clsid_
+ progID = _get(cls, '_reg_progid_')
+ desc = _get(cls, '_reg_desc_', progID)
+ spec = _get(cls, '_reg_class_spec_')
+ verProgID = _get(cls, '_reg_verprogid_')
+ defIcon = _get(cls, '_reg_icon_')
+ threadingModel = _get(cls, '_reg_threading_', 'both')
+ catids = _get(cls, '_reg_catids_', [])
+ options = _get(cls, '_reg_options_', {})
+ policySpec = _get(cls, '_reg_policy_spec_')
+ clsctx = _get(cls, '_reg_clsctx_')
+ tlb_filename = _get(cls, '_reg_typelib_filename_')
+ # default to being a COM category only when not frozen.
+ addPyComCat = not _get(cls, '_reg_disable_pycomcat_', pythoncom.frozen!=0)
+ addnPath = None
+ if debugging:
+ # If the class has a debugging dispatcher specified, use it, otherwise
+ # use our default dispatcher.
+ dispatcherSpec = _get(cls, '_reg_debug_dispatcher_spec_')
+ if dispatcherSpec is None:
+ dispatcherSpec = "win32com.server.dispatcher.DefaultDebugDispatcher"
+ # And remember the debugging flag as servers may wish to use it at runtime.
+ debuggingDesc = "(for debugging)"
+ options['Debugging'] = "1"
+ else:
+ dispatcherSpec = _get(cls, '_reg_dispatcher_spec_')
+ debuggingDesc = ""
+ options['Debugging'] = "0"
+
+ if spec is None:
+ moduleName = cls.__module__
+ if moduleName == '__main__':
+ # Use argv[0] to determine the module name.
+ try:
+ # Use the win32api to find the case-sensitive name
+ moduleName = os.path.splitext(win32api.FindFiles(sys.argv[0])[0][8])[0]
+ except (IndexError, win32api.error):
+ # Can't find the script file - the user must explicitely set the _reg_... attribute.
+ raise TypeError("Can't locate the script hosting the COM object - please set _reg_class_spec_ in your object")
+
+ spec = moduleName + "." + cls.__name__
+ # Frozen apps don't need their directory on sys.path
+ if not pythoncom.frozen:
+ scriptDir = os.path.split(sys.argv[0])[0]
+ if not scriptDir: scriptDir = "."
+ addnPath = win32api.GetFullPathName(scriptDir)
+
+ RegisterServer(clsid, spec, desc, progID, verProgID, defIcon,
+ threadingModel, policySpec, catids, options,
+ addPyComCat, dispatcherSpec, clsctx, addnPath)
+ if not quiet:
+ print('Registered:', progID or spec, debuggingDesc)
+ # Register the typelibrary
+ if tlb_filename:
+ tlb_filename = os.path.abspath(tlb_filename)
+ typelib = pythoncom.LoadTypeLib(tlb_filename)
+ pythoncom.RegisterTypeLib(typelib, tlb_filename)
+ if not quiet:
+ print('Registered type library:', tlb_filename)
+ extra = flags.get('finalize_register')
+ if extra:
+ extra()
+
+def UnregisterClasses(*classes, **flags):
+ quiet = 'quiet' in flags and flags['quiet']
+ for cls in classes:
+ clsid = cls._reg_clsid_
+ progID = _get(cls, '_reg_progid_')
+ verProgID = _get(cls, '_reg_verprogid_')
+ customKeys = _get(cls, '_reg_remove_keys_')
+ unregister_typelib = _get(cls, '_reg_typelib_filename_') is not None
+
+ UnregisterServer(clsid, progID, verProgID, customKeys)
+ if not quiet:
+ print('Unregistered:', progID or str(clsid))
+ if unregister_typelib:
+ tlb_guid = _get(cls, "_typelib_guid_")
+ if tlb_guid is None:
+ # I guess I could load the typelib, but they need the GUID anyway.
+ print("Have typelib filename, but no GUID - can't unregister")
+ else:
+ major, minor = _get(cls, "_typelib_version_", (1,0))
+ lcid = _get(cls, "_typelib_lcid_", 0)
+ try:
+ pythoncom.UnRegisterTypeLib(tlb_guid, major, minor, lcid)
+ if not quiet:
+ print('Unregistered type library')
+ except pythoncom.com_error:
+ pass
+
+ extra = flags.get('finalize_unregister')
+ if extra:
+ extra()
+#
+# Unregister info is for installers or external uninstallers.
+# The WISE installer, for example firstly registers the COM server,
+# then queries for the Unregister info, appending it to its
+# install log. Uninstalling the package will the uninstall the server
+def UnregisterInfoClasses(*classes, **flags):
+ ret = []
+ for cls in classes:
+ clsid = cls._reg_clsid_
+ progID = _get(cls, '_reg_progid_')
+ verProgID = _get(cls, '_reg_verprogid_')
+ customKeys = _get(cls, '_reg_remove_keys_')
+
+ ret = ret + GetUnregisterServerKeys(clsid, progID, verProgID, customKeys)
+ return ret
+
+# Attempt to 're-execute' our current process with elevation.
+def ReExecuteElevated(flags):
+ from win32com.shell.shell import ShellExecuteEx
+ from win32com.shell import shellcon
+ import win32process, win32event
+ import winxpgui # we've already checked we are running XP above
+ import tempfile
+
+ if not flags['quiet']:
+ print("Requesting elevation and retrying...")
+ new_params = " ".join(['"' + a + '"' for a in sys.argv])
+ # If we aren't already in unattended mode, we want our sub-process to
+ # be.
+ if not flags['unattended']:
+ new_params += " --unattended"
+ # specifying the parent means the dialog is centered over our window,
+ # which is a good usability clue.
+ # hwnd is unlikely on the command-line, but flags may come from elsewhere
+ hwnd = flags.get('hwnd', None)
+ if hwnd is None:
+ try:
+ hwnd = winxpgui.GetConsoleWindow()
+ except winxpgui.error:
+ hwnd = 0
+ # Redirect output so we give the user some clue what went wrong. This
+ # also means we need to use COMSPEC. However, the "current directory"
+ # appears to end up ignored - so we execute things via a temp batch file.
+ tempbase = tempfile.mktemp("pycomserverreg")
+ outfile = tempbase + ".out"
+ batfile = tempbase + ".bat"
+
+ # If registering from pythonwin, need to run python console instead since
+ # pythonwin will just open script for editting
+ current_exe = os.path.split(sys.executable)[1].lower()
+ exe_to_run = None
+ if current_exe == 'pythonwin.exe':
+ exe_to_run = os.path.join(sys.prefix, 'python.exe')
+ elif current_exe == 'pythonwin_d.exe':
+ exe_to_run = os.path.join(sys.prefix, 'python_d.exe')
+ if not exe_to_run or not os.path.exists(exe_to_run):
+ exe_to_run = sys.executable
+
+ try:
+ batf = open(batfile, "w")
+ try:
+ cwd = os.getcwd()
+ print("@echo off", file=batf)
+ # nothing is 'inherited' by the elevated process, including the
+ # environment. I wonder if we need to set more?
+ print("set PYTHONPATH=%s" % os.environ.get('PYTHONPATH', ''), file=batf)
+ # may be on a different drive - select that before attempting to CD.
+ print(os.path.splitdrive(cwd)[0], file=batf)
+ print('cd "%s"' % os.getcwd(), file=batf)
+ print('%s %s > "%s" 2>&1' % (win32api.GetShortPathName(exe_to_run), new_params, outfile), file=batf)
+ finally:
+ batf.close()
+ executable = os.environ.get('COMSPEC', 'cmd.exe')
+ rc = ShellExecuteEx(hwnd=hwnd,
+ fMask=shellcon.SEE_MASK_NOCLOSEPROCESS,
+ lpVerb="runas",
+ lpFile=executable,
+ lpParameters='/C "%s"' % batfile,
+ nShow=win32con.SW_SHOW)
+ hproc = rc['hProcess']
+ win32event.WaitForSingleObject(hproc, win32event.INFINITE)
+ exit_code = win32process.GetExitCodeProcess(hproc)
+ outf = open(outfile)
+ try:
+ output = outf.read()
+ finally:
+ outf.close()
+
+ if exit_code:
+ # Even if quiet you get to see this message.
+ print("Error: registration failed (exit code %s)." % exit_code)
+ # if we are quiet then the output if likely to already be nearly
+ # empty, so always print it.
+ print(output, end=' ')
+ finally:
+ for f in (outfile, batfile):
+ try:
+ os.unlink(f)
+ except os.error as exc:
+ print("Failed to remove tempfile '%s': %s" % (f, exc))
+
+def UseCommandLine(*classes, **flags):
+ unregisterInfo = '--unregister_info' in sys.argv
+ unregister = '--unregister' in sys.argv
+ flags['quiet'] = flags.get('quiet',0) or '--quiet' in sys.argv
+ flags['debug'] = flags.get('debug',0) or '--debug' in sys.argv
+ flags['unattended'] = flags.get('unattended',0) or '--unattended' in sys.argv
+ if unregisterInfo:
+ return UnregisterInfoClasses(*classes, **flags)
+ try:
+ if unregister:
+ UnregisterClasses(*classes, **flags)
+ else:
+ RegisterClasses(*classes, **flags)
+ except win32api.error as exc:
+ # If we are on xp+ and have "access denied", retry using
+ # ShellExecuteEx with 'runas' verb to force elevation (vista) and/or
+ # admin login dialog (vista/xp)
+ if flags['unattended'] or exc.winerror != winerror.ERROR_ACCESS_DENIED \
+ or sys.getwindowsversion()[0] < 5:
+ raise
+ ReExecuteElevated(flags)
+
+def RegisterPyComCategory():
+ """ Register the Python COM Server component category.
+ """
+ regCat = _cat_registrar()
+ regCat.RegisterCategories( [ (CATID_PythonCOMServer,
+ 0x0409,
+ "Python COM Server") ] )
+
+if not pythoncom.frozen:
+ try:
+ win32api.RegQueryValue(win32con.HKEY_CLASSES_ROOT,
+ 'Component Categories\\%s' % CATID_PythonCOMServer)
+ except win32api.error:
+ try:
+ RegisterPyComCategory()
+ except pythoncom.error: # Error with the COM category manager - oh well.
+ pass
+
diff --git a/Lib/site-packages/win32com/server/util.py b/Lib/site-packages/win32com/server/util.py
new file mode 100644
index 0000000..94fd218
--- /dev/null
+++ b/Lib/site-packages/win32com/server/util.py
@@ -0,0 +1,220 @@
+""" General Server side utilities
+"""
+import pythoncom
+from . import policy
+import winerror
+from .exception import COMException
+
+def wrap(ob, iid=None, usePolicy=None, useDispatcher=None):
+ """Wraps an object in a PyGDispatch gateway.
+
+ Returns a client side PyI{iid} interface.
+
+ Interface and gateway support must exist for the specified IID, as
+ the QueryInterface() method is used.
+
+ """
+ if usePolicy is None:
+ usePolicy = policy.DefaultPolicy
+ if useDispatcher == 1: # True will also work here.
+ import win32com.server.dispatcher
+ useDispatcher = win32com.server.dispatcher.DefaultDebugDispatcher
+ if useDispatcher is None or useDispatcher==0:
+ ob = usePolicy(ob)
+ else:
+ ob = useDispatcher(usePolicy, ob)
+
+ # get a PyIDispatch, which interfaces to PyGDispatch
+ ob = pythoncom.WrapObject(ob)
+ if iid is not None:
+ ob = ob.QueryInterface(iid) # Ask the PyIDispatch if it supports it?
+ return ob
+
+def unwrap(ob):
+ """Unwraps an interface.
+
+ Given an interface which wraps up a Gateway, return the object behind
+ the gateway.
+ """
+ ob = pythoncom.UnwrapObject(ob)
+ # see if the object is a dispatcher
+ if hasattr(ob, 'policy'):
+ ob = ob.policy
+ return ob._obj_
+
+
+class ListEnumerator:
+ """A class to expose a Python sequence as an EnumVARIANT.
+
+ Create an instance of this class passing a sequence (list, tuple, or
+ any sequence protocol supporting object) and it will automatically
+ support the EnumVARIANT interface for the object.
+
+ See also the @NewEnum@ function, which can be used to turn the
+ instance into an actual COM server.
+ """
+ _public_methods_ = [ 'Next', 'Skip', 'Reset', 'Clone' ]
+
+ def __init__(self, data, index=0, iid = pythoncom.IID_IEnumVARIANT):
+ self._list_ = data
+ self.index = index
+ self._iid_ = iid
+
+ def _query_interface_(self, iid):
+ if iid == self._iid_:
+ return 1
+ def Next(self, count):
+ result = self._list_[self.index:self.index+count]
+ self.Skip(count)
+ return result
+
+ def Skip(self, count):
+ end = self.index + count
+ if end > len(self._list_):
+ end = len(self._list_)
+ self.index = end
+
+ def Reset(self):
+ self.index = 0
+
+ def Clone(self):
+ return self._wrap(self.__class__(self._list_, self.index))
+
+ def _wrap(self, ob):
+ return wrap(ob)
+
+
+class ListEnumeratorGateway(ListEnumerator):
+ """A List Enumerator which wraps a sequence's items in gateways.
+
+ If a sequence contains items (objects) that have not been wrapped for
+ return through the COM layers, then a ListEnumeratorGateway can be
+ used to wrap those items before returning them (from the Next() method).
+
+ See also the @ListEnumerator@ class and the @NewEnum@ function.
+ """
+
+ def Next(self, count):
+ result = self._list_[self.index:self.index+count]
+ self.Skip(count)
+ return map(self._wrap, result)
+
+
+def NewEnum(seq,
+ cls=ListEnumerator,
+ iid=pythoncom.IID_IEnumVARIANT,
+ usePolicy=None,
+ useDispatcher=None):
+ """Creates a new enumerator COM server.
+
+ This function creates a new COM Server that implements the
+ IID_IEnumVARIANT interface.
+
+ A COM server that can enumerate the passed in sequence will be
+ created, then wrapped up for return through the COM framework.
+ Optionally, a custom COM server for enumeration can be passed
+ (the default is @ListEnumerator@), and the specific IEnum
+ interface can be specified.
+ """
+ ob = cls(seq, iid=iid)
+ return wrap(ob, iid, usePolicy=usePolicy, useDispatcher=useDispatcher)
+
+
+class Collection:
+ "A collection of VARIANT values."
+
+ _public_methods_ = [ 'Item', 'Count', 'Add', 'Remove', 'Insert' ]
+
+ def __init__(self, data=None, readOnly=0):
+ if data is None:
+ data = [ ]
+ self.data = data
+
+ # disable Add/Remove if read-only. note that we adjust _public_methods_
+ # on this instance only.
+ if readOnly:
+ self._public_methods_ = [ 'Item', 'Count' ]
+
+ # This method is also used as the "default" method.
+ # Thus "print ob" will cause this to be called with zero
+ # params. Handle this slightly more elegantly here.
+ # Ideally the policy should handle this.
+ def Item(self, *args):
+ if len(args) != 1:
+ raise COMException(scode=winerror.DISP_E_BADPARAMCOUNT)
+
+ try:
+ return self.data[args[0]]
+ except IndexError as desc:
+ raise COMException(scode=winerror.DISP_E_BADINDEX, desc=str(desc))
+
+
+ _value_ = Item
+
+ def Count(self):
+ return len(self.data)
+
+ def Add(self, value):
+ self.data.append(value)
+
+ def Remove(self, index):
+ try:
+ del self.data[index]
+ except IndexError as desc:
+ raise COMException(scode=winerror.DISP_E_BADINDEX, desc=str(desc))
+
+ def Insert(self, index, value):
+ try:
+ index = int(index)
+ except (ValueError, TypeError):
+ raise COMException(scode=winerror.DISP_E_TYPEMISMATCH)
+ self.data.insert(index, value)
+
+ def _NewEnum(self):
+ return NewEnum(self.data)
+
+def NewCollection(seq, cls=Collection):
+ """Creates a new COM collection object
+
+ This function creates a new COM Server that implements the
+ common collection protocols, including enumeration. (_NewEnum)
+
+ A COM server that can enumerate the passed in sequence will be
+ created, then wrapped up for return through the COM framework.
+ Optionally, a custom COM server for enumeration can be passed
+ (the default is @Collection@).
+ """
+ return pythoncom.WrapObject(policy.DefaultPolicy(cls(seq)),
+ pythoncom.IID_IDispatch,
+ pythoncom.IID_IDispatch)
+
+class FileStream:
+ _public_methods_ = [ 'Read', 'Write', 'Clone', 'CopyTo', 'Seek' ]
+ _com_interfaces_ = [ pythoncom.IID_IStream ]
+
+ def __init__(self, file):
+ self.file = file
+
+ def Read(self, amount):
+ return self.file.read(amount)
+
+ def Write(self, data):
+ self.file.write(data)
+ return len(data)
+
+ def Clone(self):
+ return self._wrap(self.__class__(self.file))
+
+ def CopyTo(self, dest, cb):
+ data=self.file.read(cb)
+ cbread=len(data)
+ dest.Write(data) ## ??? Write does not currently return the length ???
+ return cbread, cbread
+
+ def Seek(self, offset, origin):
+ # how convient that the 'origin' values are the same as the CRT :)
+ self.file.seek(offset, origin)
+ return self.file.tell()
+
+ def _wrap(self, ob):
+ return wrap(ob)
diff --git a/Lib/site-packages/win32com/servers/PythonTools.py b/Lib/site-packages/win32com/servers/PythonTools.py
new file mode 100644
index 0000000..ac2e805
--- /dev/null
+++ b/Lib/site-packages/win32com/servers/PythonTools.py
@@ -0,0 +1,45 @@
+import sys
+import time
+
+class Tools:
+ _public_methods_ = [ 'reload', 'adddir', 'echo', 'sleep' ]
+
+ def reload(self, module):
+ if module in sys.modules:
+ try:
+ from imp import reload
+ except ImportError:
+ pass # builtin in py2k
+ reload(sys.modules[module])
+ return "reload succeeded."
+ return "no reload performed."
+
+ def adddir(self, dir):
+ if type(dir) == type(''):
+ sys.path.append(dir)
+ return str(sys.path)
+
+ def echo(self, arg):
+ return repr(arg)
+
+ def sleep(self, t):
+ time.sleep(t)
+
+
+if __name__=='__main__':
+ from win32com.server.register import RegisterServer, UnregisterServer
+ clsid = "{06ce7630-1d81-11d0-ae37-c2fa70000000}"
+ progid = "Python.Tools"
+ verprogid = "Python.Tools.1"
+ if "--unregister" in sys.argv:
+ print("Unregistering...")
+ UnregisterServer(clsid, progid, verprogid)
+ print("Unregistered OK")
+ else:
+ print("Registering COM server...")
+ RegisterServer(clsid,
+ "win32com.servers.PythonTools.Tools",
+ "Python Tools",
+ progid,
+ verprogid)
+ print("Class registered.")
diff --git a/Lib/site-packages/win32com/servers/__init__.py b/Lib/site-packages/win32com/servers/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/Lib/site-packages/win32com/servers/dictionary.py b/Lib/site-packages/win32com/servers/dictionary.py
new file mode 100644
index 0000000..99ae295
--- /dev/null
+++ b/Lib/site-packages/win32com/servers/dictionary.py
@@ -0,0 +1,130 @@
+"""Python.Dictionary COM Server.
+
+This module implements a simple COM server that acts much like a Python
+dictionary or as a standard string-keyed VB Collection. The keys of
+the dictionary are strings and are case-insensitive.
+
+It uses a highly customized policy to fine-tune the behavior exposed to
+the COM client.
+
+The object exposes the following properties:
+
+ int Count (readonly)
+ VARIANT Item(BSTR key) (propget for Item)
+ Item(BSTR key, VARIANT value) (propput for Item)
+
+ Note that 'Item' is the default property, so the following forms of
+ VB code are acceptable:
+
+ set ob = CreateObject("Python.Dictionary")
+ ob("hello") = "there"
+ ob.Item("hi") = ob("HELLO")
+
+All keys are defined, returning VT_NULL (None) if a value has not been
+stored. To delete a key, simply assign VT_NULL to the key.
+
+The object responds to the _NewEnum method by returning an enumerator over
+the dictionary's keys. This allows for the following type of VB code:
+
+ for each name in ob
+ debug.print name, ob(name)
+ next
+"""
+
+import pythoncom
+from win32com.server import util, policy
+from win32com.server.exception import COMException
+import winerror
+import types
+import pywintypes
+
+from pythoncom import DISPATCH_METHOD, DISPATCH_PROPERTYGET
+from winerror import S_OK
+
+class DictionaryPolicy(policy.BasicWrapPolicy):
+ ### BasicWrapPolicy looks for this
+ _com_interfaces_ = [ ]
+
+ ### BasicWrapPolicy looks for this
+ _name_to_dispid_ = {
+ 'item' : pythoncom.DISPID_VALUE,
+ '_newenum' : pythoncom.DISPID_NEWENUM,
+ 'count' : 1,
+ }
+
+ ### Auto-Registration process looks for these...
+ _reg_desc_ = 'Python Dictionary'
+ _reg_clsid_ = '{39b61048-c755-11d0-86fa-00c04fc2e03e}'
+ _reg_progid_ = 'Python.Dictionary'
+ _reg_verprogid_ = 'Python.Dictionary.1'
+ _reg_policy_spec_ = 'win32com.servers.dictionary.DictionaryPolicy'
+
+ def _CreateInstance_(self, clsid, reqIID):
+ self._wrap_({ })
+ return pythoncom.WrapObject(self, reqIID)
+
+ def _wrap_(self, ob):
+ self._obj_ = ob # ob should be a dictionary
+
+ def _invokeex_(self, dispid, lcid, wFlags, args, kwargs, serviceProvider):
+ if dispid == 0: # item
+ l = len(args)
+ if l < 1:
+ raise COMException(desc="not enough parameters", scode=winerror.DISP_E_BADPARAMCOUNT)
+
+ key = args[0]
+ if type(key) not in [str, str]:
+ ### the nArgErr thing should be 0-based, not reversed... sigh
+ raise COMException(desc="Key must be a string", scode=winerror.DISP_E_TYPEMISMATCH)
+
+ key = key.lower()
+
+ if wFlags & (DISPATCH_METHOD | DISPATCH_PROPERTYGET):
+ if l > 1:
+ raise COMException(scode=winerror.DISP_E_BADPARAMCOUNT)
+ try:
+ return self._obj_[key]
+ except KeyError:
+ return None # unknown keys return None (VT_NULL)
+
+ if l != 2:
+ raise COMException(scode=winerror.DISP_E_BADPARAMCOUNT)
+ if args[1] is None:
+ # delete a key when None is assigned to it
+ try:
+ del self._obj_[key]
+ except KeyError:
+ pass
+ else:
+ self._obj_[key] = args[1]
+ return S_OK
+
+ if dispid == 1: # count
+ if not wFlags & DISPATCH_PROPERTYGET:
+ raise COMException(scode=winerror.DISP_E_MEMBERNOTFOUND) # not found
+ if len(args) != 0:
+ raise COMException(scode=winerror.DISP_E_BADPARAMCOUNT)
+ return len(self._obj_)
+
+ if dispid == pythoncom.DISPID_NEWENUM:
+ return util.NewEnum(list(self._obj_.keys()))
+
+ raise COMException(scode=winerror.DISP_E_MEMBERNOTFOUND)
+
+ def _getidsofnames_(self, names, lcid):
+ ### this is a copy of MappedWrapPolicy._getidsofnames_ ...
+
+ name = names[0].lower()
+ try:
+ return (self._name_to_dispid_[name],)
+ except KeyError:
+ raise COMException(scode=winerror.DISP_E_MEMBERNOTFOUND,
+ desc="Member not found")
+
+
+def Register():
+ from win32com.server.register import UseCommandLine
+ return UseCommandLine(DictionaryPolicy)
+
+if __name__ == '__main__':
+ Register()
diff --git a/Lib/site-packages/win32com/servers/interp.py b/Lib/site-packages/win32com/servers/interp.py
new file mode 100644
index 0000000..8a4e592
--- /dev/null
+++ b/Lib/site-packages/win32com/servers/interp.py
@@ -0,0 +1,52 @@
+"""Python.Interpreter COM Server
+
+ This module implements a very very simple COM server which
+ exposes the Python interpreter.
+
+ This is designed more as a demonstration than a full blown COM server.
+ General functionality and Error handling are both limited.
+
+ To use this object, ensure it is registered by running this module
+ from Python.exe. Then, from Visual Basic, use "CreateObject('Python.Interpreter')",
+ and call its methods!
+"""
+
+from win32com.server.exception import Exception
+import winerror
+
+# Expose the Python interpreter.
+class Interpreter:
+ """The interpreter object exposed via COM
+ """
+ _public_methods_ = [ 'Exec', 'Eval' ]
+ # All registration stuff to support fully automatic register/unregister
+ _reg_verprogid_ = "Python.Interpreter.2"
+ _reg_progid_ = "Python.Interpreter"
+ _reg_desc_ = "Python Interpreter"
+ _reg_clsid_ = "{30BD3490-2632-11cf-AD5B-524153480001}"
+ _reg_class_spec_ = "win32com.servers.interp.Interpreter"
+
+ def __init__(self):
+ self.dict = {}
+
+ def Eval(self, exp):
+ """Evaluate an expression.
+ """
+ if type(exp) not in [str, str]:
+ raise Exception(desc="Must be a string",scode=winerror.DISP_E_TYPEMISMATCH)
+
+ return eval(str(exp), self.dict)
+ def Exec(self, exp):
+ """Execute a statement.
+ """
+ if type(exp) not in [str, str]:
+ raise Exception(desc="Must be a string",scode=winerror.DISP_E_TYPEMISMATCH)
+ exec(str(exp), self.dict)
+
+def Register():
+ import win32com.server.register
+ return win32com.server.register.UseCommandLine(Interpreter)
+
+if __name__=='__main__':
+ print("Registering COM server...")
+ Register()
diff --git a/Lib/site-packages/win32com/servers/perfmon.py b/Lib/site-packages/win32com/servers/perfmon.py
new file mode 100644
index 0000000..3b31186
--- /dev/null
+++ b/Lib/site-packages/win32com/servers/perfmon.py
@@ -0,0 +1,27 @@
+"""A COM Server which exposes the NT Performance monitor in a very rudimentary way
+
+Usage from VB:
+ set ob = CreateObject("Python.PerfmonQuery")
+ freeBytes = ob.Query("Memory", "Available Bytes")
+"""
+from win32com.server import exception, register
+import pythoncom, win32pdhutil, winerror
+
+class PerfMonQuery:
+ _reg_verprogid_ = "Python.PerfmonQuery.1"
+ _reg_progid_ = "Python.PerfmonQuery"
+ _reg_desc_ = "Python Performance Monitor query object"
+ _reg_clsid_ = "{64cef7a0-8ece-11d1-a65a-00aa00125a98}"
+ _reg_class_spec_ = "win32com.servers.perfmon.PerfMonQuery"
+ _public_methods_ = [ 'Query' ]
+ def Query(self, object, counter, instance = None, machine = None):
+ try:
+ return win32pdhutil.GetPerformanceAttributes(object, counter, instance, machine=machine)
+ except win32pdhutil.error as exc:
+ raise exception.Exception(desc=exc.strerror)
+ except TypeError as desc:
+ raise exception.Exception(desc=desc,scode=winerror.DISP_E_TYPEMISMATCH)
+
+if __name__=='__main__':
+ print("Registering COM server...")
+ register.UseCommandLine(PerfMonQuery)
diff --git a/Lib/site-packages/win32com/servers/test_pycomtest.py b/Lib/site-packages/win32com/servers/test_pycomtest.py
new file mode 100644
index 0000000..3212f9e
--- /dev/null
+++ b/Lib/site-packages/win32com/servers/test_pycomtest.py
@@ -0,0 +1,168 @@
+# This is part of the Python test suite.
+# The object is registered when you first run the test suite.
+# (and hopefully unregistered once done ;-)
+
+# Ensure the vtables in the tlb are known.
+from win32com import universal
+from win32com.server.exception import COMException
+from win32com.client import gencache
+import winerror
+from win32com.client import constants
+from win32com.server.util import wrap
+
+import pythoncom
+pythoncom.__future_currency__ = True
+# We use the constants from the module, so must insist on a gencache.
+# Otherwise, use of gencache is not necessary (tho still advised)
+gencache.EnsureModule('{6BCDCB60-5605-11D0-AE5F-CADD4C000000}', 0, 1, 1)
+
+class PyCOMTest:
+ _typelib_guid_ = "{6BCDCB60-5605-11D0-AE5F-CADD4C000000}"
+ _typelib_version = 1,0
+ _com_interfaces_ = ['IPyCOMTest']
+ _reg_clsid_ = "{e743d9cd-cb03-4b04-b516-11d3a81c1597}"
+ _reg_progid_ = "Python.Test.PyCOMTest"
+
+ def DoubleString(self, str):
+ return str*2
+ def DoubleInOutString(self, str):
+ return str*2
+
+ def Fire(self, nID):
+ raise COMException(hresult=winerror.E_NOTIMPL)
+
+ def GetLastVarArgs(self):
+ raise COMException(hresult=winerror.E_NOTIMPL)
+
+ def GetMultipleInterfaces(self, outinterface1, outinterface2):
+ raise COMException(hresult=winerror.E_NOTIMPL)
+
+ def GetSafeArrays(self, attrs, attrs2, ints):
+ raise COMException(hresult=winerror.E_NOTIMPL)
+
+ def GetSetDispatch(self, indisp):
+ raise COMException(hresult=winerror.E_NOTIMPL)
+
+ # Result is of type IPyCOMTest
+ def GetSetInterface(self, ininterface):
+ return wrap(self)
+
+ def GetSetVariant(self, indisp):
+ return indisp
+
+ def TestByRefVariant(self, v):
+ return v * 2
+
+ def TestByRefString(self, v):
+ return v * 2
+
+ # Result is of type IPyCOMTest
+ def GetSetInterfaceArray(self, ininterface):
+ raise COMException(hresult=winerror.E_NOTIMPL)
+
+ def GetSetUnknown(self, inunk):
+ raise COMException(hresult=winerror.E_NOTIMPL)
+
+ # Result is of type ISimpleCounter
+ def GetSimpleCounter(self):
+ raise COMException(hresult=winerror.E_NOTIMPL)
+
+ def GetSimpleSafeArray(self, ints):
+ raise COMException(hresult=winerror.E_NOTIMPL)
+
+ def GetStruct(self):
+ raise COMException(hresult=winerror.E_NOTIMPL)
+
+ def SetIntSafeArray(self, ints):
+ return len(ints)
+
+ def SetLongLongSafeArray(self, ints):
+ return len(ints)
+
+ def SetULongLongSafeArray(self, ints):
+ return len(ints)
+
+ def SetBinSafeArray(self, buf):
+ return len(buf)
+
+ def SetVarArgs(self, *args):
+ raise COMException(hresult=winerror.E_NOTIMPL)
+
+ def SetVariantSafeArray(self, vars):
+ raise COMException(hresult=winerror.E_NOTIMPL)
+
+ def Start(self):
+ raise COMException(hresult=winerror.E_NOTIMPL)
+
+ def Stop(self, nID):
+ raise COMException(hresult=winerror.E_NOTIMPL)
+
+ def StopAll(self):
+ raise COMException(hresult=winerror.E_NOTIMPL)
+
+ def TakeByRefDispatch(self, inout):
+ raise COMException(hresult=winerror.E_NOTIMPL)
+
+ def TakeByRefTypedDispatch(self, inout):
+ raise COMException(hresult=winerror.E_NOTIMPL)
+
+ def Test(self, key, inval):
+ return not inval
+
+ def Test2(self, inval):
+ return inval
+
+ def Test3(self, inval):
+ raise COMException(hresult=winerror.E_NOTIMPL)
+
+ def Test4(self, inval):
+ raise COMException(hresult=winerror.E_NOTIMPL)
+
+ def Test5(self, inout):
+ if inout == constants.TestAttr1:
+ return constants.TestAttr1_1
+ elif inout == constants.TestAttr1_1:
+ return constants.TestAttr1
+ else:
+ return -1
+
+ def Test6(self, inval):
+ return inval
+
+ def TestOptionals(self, strArg='def', sval=0, lval=1, dval=3.1400001049041748):
+ raise COMException(hresult=winerror.E_NOTIMPL)
+
+ def TestOptionals2(self, dval, strval='', sval=1):
+ raise COMException(hresult=winerror.E_NOTIMPL)
+
+ def CheckVariantSafeArray(self, data):
+ return 1
+
+ def LongProp(self):
+ return self.longval
+ def SetLongProp(self, val):
+ self.longval = val
+ def ULongProp(self):
+ return self.ulongval
+ def SetULongProp(self, val):
+ self.ulongval = val
+ def IntProp(self):
+ return self.intval
+ def SetIntProp(self, val):
+ self.intval = val
+
+class PyCOMTestMI(PyCOMTest):
+ _typelib_guid_ = "{6BCDCB60-5605-11D0-AE5F-CADD4C000000}"
+ _typelib_version = 1,0
+ # Interfaces with a interface name, a real IID, and an IID as a string
+ _com_interfaces_ = ['IPyCOMTest',
+ pythoncom.IID_IStream,
+ str(pythoncom.IID_IStorage),
+ ]
+ _reg_clsid_ = "{F506E9A1-FB46-4238-A597-FA4EB69787CA}"
+ _reg_progid_ = "Python.Test.PyCOMTestMI"
+
+if __name__ == '__main__':
+ import win32com.server.register
+ win32com.server.register.UseCommandLine(PyCOMTest)
+ win32com.server.register.UseCommandLine(PyCOMTestMI)
diff --git a/Lib/site-packages/win32com/storagecon.py b/Lib/site-packages/win32com/storagecon.py
new file mode 100644
index 0000000..627745e
--- /dev/null
+++ b/Lib/site-packages/win32com/storagecon.py
@@ -0,0 +1,142 @@
+"""Constants related to IStorage and related interfaces
+
+This file was generated by h2py from d:\msdev\include\objbase.h
+then hand edited, a few extra constants added, etc.
+"""
+
+STGC_DEFAULT = 0
+STGC_OVERWRITE = 1
+STGC_ONLYIFCURRENT = 2
+STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE = 4
+STGC_CONSOLIDATE = 8
+
+STGTY_STORAGE = 1
+STGTY_STREAM = 2
+STGTY_LOCKBYTES = 3
+STGTY_PROPERTY = 4
+STREAM_SEEK_SET = 0
+STREAM_SEEK_CUR = 1
+STREAM_SEEK_END = 2
+
+LOCK_WRITE = 1
+LOCK_EXCLUSIVE = 2
+LOCK_ONLYONCE = 4
+
+# Generated as from here.
+
+CWCSTORAGENAME = 32
+STGM_DIRECT = 0x00000000
+STGM_TRANSACTED = 0x00010000
+STGM_SIMPLE = 0x08000000
+STGM_READ = 0x00000000
+STGM_WRITE = 0x00000001
+STGM_READWRITE = 0x00000002
+STGM_SHARE_DENY_NONE = 0x00000040
+STGM_SHARE_DENY_READ = 0x00000030
+STGM_SHARE_DENY_WRITE = 0x00000020
+STGM_SHARE_EXCLUSIVE = 0x00000010
+STGM_PRIORITY = 0x00040000
+STGM_DELETEONRELEASE = 0x04000000
+STGM_NOSCRATCH = 0x00100000
+STGM_CREATE = 0x00001000
+STGM_CONVERT = 0x00020000
+STGM_FAILIFTHERE = 0x00000000
+STGM_NOSNAPSHOT = 0x00200000
+ASYNC_MODE_COMPATIBILITY = 0x00000001
+ASYNC_MODE_DEFAULT = 0x00000000
+STGTY_REPEAT = 0x00000100
+STG_TOEND = 0xFFFFFFFF
+STG_LAYOUT_SEQUENTIAL = 0x00000000
+STG_LAYOUT_INTERLEAVED = 0x00000001
+
+## access rights used with COM server ACL's
+COM_RIGHTS_EXECUTE = 1
+COM_RIGHTS_EXECUTE_LOCAL = 2
+COM_RIGHTS_EXECUTE_REMOTE = 4
+COM_RIGHTS_ACTIVATE_LOCAL = 8
+COM_RIGHTS_ACTIVATE_REMOTE = 16
+
+STGFMT_DOCUMENT = 0
+STGFMT_STORAGE = 0
+STGFMT_NATIVE = 1
+STGFMT_FILE = 3
+STGFMT_ANY = 4
+STGFMT_DOCFILE = 5
+
+PID_DICTIONARY = 0
+PID_CODEPAGE = 1
+PID_FIRST_USABLE = 2
+PID_FIRST_NAME_DEFAULT = 4095
+
+PID_LOCALE = -2147483648
+PID_MODIFY_TIME = -2147483647
+PID_SECURITY = -2147483646
+PID_BEHAVIOR = -2147483645
+PID_ILLEGAL = -1
+PID_MIN_READONLY = -2147483648
+PID_MAX_READONLY = -1073741825
+
+## DiscardableInformation
+PIDDI_THUMBNAIL = 0x00000002
+
+## SummaryInformation
+PIDSI_TITLE = 2
+PIDSI_SUBJECT = 3
+PIDSI_AUTHOR = 4
+PIDSI_KEYWORDS = 5
+PIDSI_COMMENTS = 6
+PIDSI_TEMPLATE = 7
+PIDSI_LASTAUTHOR = 8
+PIDSI_REVNUMBER = 9
+PIDSI_EDITTIME = 10
+PIDSI_LASTPRINTED = 11
+PIDSI_CREATE_DTM = 12
+PIDSI_LASTSAVE_DTM = 13
+PIDSI_PAGECOUNT = 14
+PIDSI_WORDCOUNT = 15
+PIDSI_CHARCOUNT = 16
+PIDSI_THUMBNAIL = 17
+PIDSI_APPNAME = 18
+PIDSI_DOC_SECURITY = 19
+
+## DocSummaryInformation
+PIDDSI_CATEGORY = 2
+PIDDSI_PRESFORMAT = 3
+PIDDSI_BYTECOUNT = 4
+PIDDSI_LINECOUNT = 5
+PIDDSI_PARCOUNT = 6
+PIDDSI_SLIDECOUNT = 7
+PIDDSI_NOTECOUNT = 8
+PIDDSI_HIDDENCOUNT = 9
+PIDDSI_MMCLIPCOUNT = 10
+PIDDSI_SCALE = 11
+PIDDSI_HEADINGPAIR = 12
+PIDDSI_DOCPARTS = 13
+PIDDSI_MANAGER = 14
+PIDDSI_COMPANY = 15
+PIDDSI_LINKSDIRTY = 16
+
+
+## MediaFileSummaryInfo
+PIDMSI_EDITOR = 2
+PIDMSI_SUPPLIER = 3
+PIDMSI_SOURCE = 4
+PIDMSI_SEQUENCE_NO = 5
+PIDMSI_PROJECT = 6
+PIDMSI_STATUS = 7
+PIDMSI_OWNER = 8
+PIDMSI_RATING = 9
+PIDMSI_PRODUCTION = 10
+PIDMSI_COPYRIGHT = 11
+
+## PROPSETFLAG enum
+PROPSETFLAG_DEFAULT = 0
+PROPSETFLAG_NONSIMPLE = 1
+PROPSETFLAG_ANSI = 2
+PROPSETFLAG_UNBUFFERED = 4
+PROPSETFLAG_CASE_SENSITIVE = 8
+
+## STGMOVE enum
+STGMOVE_MOVE = 0
+STGMOVE_COPY = 1
+STGMOVE_SHALLOWCOPY = 2
diff --git a/Lib/site-packages/win32com/test/GenTestScripts.py b/Lib/site-packages/win32com/test/GenTestScripts.py
new file mode 100644
index 0000000..7aea6a8
--- /dev/null
+++ b/Lib/site-packages/win32com/test/GenTestScripts.py
@@ -0,0 +1,84 @@
+#
+# Generate scripts needed for serious testing!
+#
+import win32com, win32com.client.makepy
+import win32com.test
+import pythoncom
+import sys, os
+
+genList = [
+("msword8", "{00020905-0000-0000-C000-000000000046}",1033,8,0),
+]
+
+genDir = "Generated4Test"
+def GetGenPath():
+ import win32api
+ return os.path.join(win32api.GetFullPathName(win32com.test.__path__[0]), genDir)
+
+def GenerateFromRegistered(fname, *loadArgs):
+# tlb = apply(pythoncom.LoadRegTypeLib, loadArgs)
+ genPath = GetGenPath()
+ try:
+ os.stat(genPath)
+ except os.error:
+ os.mkdir(genPath)
+ # Ensure an __init__ exists.
+ open(os.path.join(genPath, "__init__.py"), "w").close()
+ print(fname, ": generating -", end=' ')
+ f = open(os.path.join(genPath, fname + ".py"), "w")
+ win32com.client.makepy.GenerateFromTypeLibSpec(loadArgs, f, bQuiet = 1, bGUIProgress = 1)
+ f.close()
+ print("compiling -", end=' ')
+ fullModName = "win32com.test.%s.%s" % (genDir, fname)
+ exec("import " + fullModName)
+ # Inject the generated module as a top level module.
+ sys.modules[fname] = sys.modules[fullModName]
+ print("done")
+
+
+def GenerateAll():
+ for args in genList:
+ try:
+ GenerateFromRegistered(*args)
+ except KeyboardInterrupt:
+ print("** Interrupted ***")
+ break
+ except pythoncom.com_error:
+ print("** Could not generate test code for ", args[0])
+
+def CleanAll():
+ print("Cleaning generated test scripts...")
+ try: # Clear exceptions!
+ 1/0
+ except:
+ pass
+ genPath = GetGenPath()
+ for args in genList:
+ try:
+ name = args[0]+".py"
+ os.unlink(os.path.join(genPath, name))
+ except os.error as details:
+ if type(details)==type(()) and details[0]!=2:
+ print("Could not deleted generated", name, details)
+ try:
+ name = args[0]+".pyc"
+ os.unlink(os.path.join(genPath, name))
+ except os.error as details:
+ if type(details)==type(()) and details[0]!=2:
+ print("Could not deleted generated", name, details)
+ try:
+ os.unlink(os.path.join(genPath, "__init__.py"))
+ except:
+ pass
+ try:
+ os.unlink(os.path.join(genPath, "__init__.pyc"))
+ except:
+ pass
+ try:
+ os.rmdir(genPath)
+ except os.error as details:
+ print("Could not delete test directory -", details)
+
+if __name__=='__main__':
+ GenerateAll()
+ CleanAll()
diff --git a/Lib/site-packages/win32com/test/Testpys.sct b/Lib/site-packages/win32com/test/Testpys.sct
new file mode 100644
index 0000000..3bc7ea9
--- /dev/null
+++ b/Lib/site-packages/win32com/test/Testpys.sct
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Lib/site-packages/win32com/test/__init__.py b/Lib/site-packages/win32com/test/__init__.py
new file mode 100644
index 0000000..75e7b73
--- /dev/null
+++ b/Lib/site-packages/win32com/test/__init__.py
@@ -0,0 +1 @@
+# Empty file to designate a Python package
diff --git a/Lib/site-packages/win32com/test/daodump.py b/Lib/site-packages/win32com/test/daodump.py
new file mode 100644
index 0000000..0893b06
--- /dev/null
+++ b/Lib/site-packages/win32com/test/daodump.py
@@ -0,0 +1,61 @@
+# import dao3032
+# No longer imported here - callers responsibility to load
+#
+import win32com.client
+
+def DumpDB(db, bDeep = 1):
+ # MUST be a DB object.
+ DumpTables(db,bDeep)
+ DumpRelations(db,bDeep)
+ DumpAllContainers(db,bDeep)
+
+def DumpTables(db, bDeep = 1):
+ for tab in db.TableDefs:
+ tab = db.TableDefs(tab.Name) # Redundant lookup for testing purposes.
+ print("Table %s - Fields: %d, Attributes:%d" % (tab.Name, len(tab.Fields), tab.Attributes))
+ if bDeep: DumpFields(tab.Fields)
+
+def DumpFields(fields):
+ for field in fields:
+ print(" %s, size=%d, reqd=%d, type=%d, defVal=%s" % (field.Name, field.Size, field.Required, field.Type, str(field.DefaultValue)))
+
+def DumpRelations(db, bDeep = 1):
+ for relation in db.Relations:
+ print("Relation %s - %s->%s" % (relation.Name, relation.Table, relation.ForeignTable))
+
+#### This dont work. TLB says it is a Fields collection, but apparently not!
+#### if bDeep: DumpFields(relation.Fields)
+
+def DumpAllContainers(db, bDeep = 1):
+ for cont in db.Containers:
+ print("Container %s - %d documents" % (cont.Name, len(cont.Documents)))
+ if bDeep: DumpContainerDocuments(cont)
+
+def DumpContainerDocuments(container):
+ for doc in container.Documents:
+ import time
+ timeStr = time.ctime(int(doc.LastUpdated))
+ print(" %s - updated %s (" % (doc.Name, timeStr), end=' ')
+ print(doc.LastUpdated,")") # test the _print_ method?
+
+def TestEngine(engine):
+ import sys
+ if len(sys.argv)>1:
+ dbName = sys.argv[1]
+ else:
+ dbName = "e:\\temp\\TestPython.mdb"
+ db = engine.OpenDatabase(dbName)
+ DumpDB(db)
+
+def test():
+ for progid in ("DAO.DBEngine.36", "DAO.DBEngine.35", "DAO.DBEngine.30"):
+ try:
+ ob = win32com.client.gencache.EnsureDispatch(progid)
+ except pythoncom.com_error:
+ print(progid, "does not seem to be installed")
+ else:
+ TestEngine(ob)
+ break
+
+if __name__=='__main__':
+ test()
diff --git a/Lib/site-packages/win32com/test/errorSemantics.py b/Lib/site-packages/win32com/test/errorSemantics.py
new file mode 100644
index 0000000..a80cfd3
--- /dev/null
+++ b/Lib/site-packages/win32com/test/errorSemantics.py
@@ -0,0 +1,151 @@
+# errorSemantics.py
+
+# Test the Python error handling semantics. Specifically:
+#
+# * When a Python COM object is called via IDispatch, the nominated
+# scode is placed in the exception tuple, and the HRESULT is
+# DISP_E_EXCEPTION
+# * When the same interface is called via IWhatever, the
+# nominated scode is returned directly (with the scode also
+# reflected in the exception tuple)
+# * In all cases, the description etc end up in the exception tuple
+# * "Normal" Python exceptions resolve to an E_FAIL "internal error"
+
+import pythoncom
+from win32com.server.exception import COMException
+from win32com.server.util import wrap
+from win32com.client import Dispatch
+import winerror
+from win32com.test.util import CaptureWriter
+
+class error(Exception):
+ def __init__(self, msg, com_exception=None):
+ Exception.__init__(self, msg, str(com_exception))
+
+# Our COM server.
+class TestServer:
+ _public_methods_ = [ 'Clone', 'Commit', 'LockRegion', 'Read']
+ _com_interfaces_ = [ pythoncom.IID_IStream ]
+
+ def Clone(self):
+ raise COMException("Not today", scode=winerror.E_UNEXPECTED)
+
+ def Commit(self, flags):
+ raise Exception("foo")
+
+def test():
+ # Call via a native interface.
+ com_server = wrap(TestServer(), pythoncom.IID_IStream)
+ try:
+ com_server.Clone()
+ raise error("Expecting this call to fail!")
+ except pythoncom.com_error as com_exc:
+ if com_exc.hresult != winerror.E_UNEXPECTED:
+ raise error("Calling the object natively did not yield the correct scode", com_exc)
+ exc = com_exc.excepinfo
+ if not exc or exc[-1] != winerror.E_UNEXPECTED:
+ raise error("The scode element of the exception tuple did not yield the correct scode", com_exc)
+ if exc[2] != "Not today":
+ raise error("The description in the exception tuple did not yield the correct string", com_exc)
+ cap = CaptureWriter()
+ try:
+ cap.capture()
+ try:
+ com_server.Commit(0)
+ finally:
+ cap.release()
+ raise error("Expecting this call to fail!")
+ except pythoncom.com_error as com_exc:
+ if com_exc.hresult != winerror.E_FAIL:
+ raise error("The hresult was not E_FAIL for an internal error", com_exc)
+ if com_exc.excepinfo[1] != "Python COM Server Internal Error":
+ raise error("The description in the exception tuple did not yield the correct string", com_exc)
+ # Check we saw a traceback in stderr
+ if cap.get_captured().find("Traceback")<0:
+ raise error("Could not find a traceback in stderr: %r" % (cap.get_captured(),))
+
+ # Now do it all again, but using IDispatch
+ com_server = Dispatch(wrap(TestServer()))
+ try:
+ com_server.Clone()
+ raise error("Expecting this call to fail!")
+ except pythoncom.com_error as com_exc:
+ if com_exc.hresult != winerror.DISP_E_EXCEPTION:
+ raise error("Calling the object via IDispatch did not yield the correct scode", com_exc)
+ exc = com_exc.excepinfo
+ if not exc or exc[-1] != winerror.E_UNEXPECTED:
+ raise error("The scode element of the exception tuple did not yield the correct scode", com_exc)
+ if exc[2] != "Not today":
+ raise error("The description in the exception tuple did not yield the correct string", com_exc)
+
+ cap.clear()
+ try:
+ cap.capture()
+ try:
+ com_server.Commit(0)
+ finally:
+ cap.release()
+ raise error("Expecting this call to fail!")
+ except pythoncom.com_error as com_exc:
+ if com_exc.hresult != winerror.DISP_E_EXCEPTION:
+ raise error("Calling the object via IDispatch did not yield the correct scode", com_exc)
+ exc = com_exc.excepinfo
+ if not exc or exc[-1] != winerror.E_FAIL:
+ raise error("The scode element of the exception tuple did not yield the correct scode", com_exc)
+ if exc[1] != "Python COM Server Internal Error":
+ raise error("The description in the exception tuple did not yield the correct string", com_exc)
+ # Check we saw a traceback in stderr
+ if cap.get_captured().find("Traceback")<0:
+ raise error("Could not find a traceback in stderr: %r" % (cap.get_captured(),))
+
+try:
+ import logging
+except ImportError:
+ logging = None
+if logging is not None:
+ import win32com
+ class TestLogHandler(logging.Handler):
+ def __init__(self):
+ self.num_emits = 0
+ logging.Handler.__init__(self)
+ def emit(self, record):
+ self.num_emits += 1
+ return
+ print("--- record start")
+ print(self.format(record))
+ print("--- record end")
+
+ def testLogger():
+ assert not hasattr(win32com, "logger")
+ handler = TestLogHandler()
+ formatter = logging.Formatter('%(message)s')
+ handler.setFormatter(formatter)
+ log = logging.getLogger("win32com_test")
+ log.addHandler(handler)
+ win32com.logger = log
+ # Now throw some exceptions!
+ # Native interfaces
+ com_server = wrap(TestServer(), pythoncom.IID_IStream)
+ try:
+ com_server.Commit(0)
+ raise RuntimeError("should have failed")
+ except pythoncom.error:
+ pass
+ assert handler.num_emits == 1, handler.num_emits
+ handler.num_emits = 0 # reset
+
+ com_server = Dispatch(wrap(TestServer()))
+ try:
+ com_server.Commit(0)
+ raise RuntimeError("should have failed")
+ except pythoncom.error:
+ pass
+ assert handler.num_emits == 1, handler.num_emits
+
+if __name__=='__main__':
+ test()
+ if logging is not None:
+ testLogger()
+ from .util import CheckClean
+ CheckClean()
+ print("error semantic tests worked")
diff --git a/Lib/site-packages/win32com/test/pippo.idl b/Lib/site-packages/win32com/test/pippo.idl
new file mode 100644
index 0000000..2132e0c
--- /dev/null
+++ b/Lib/site-packages/win32com/test/pippo.idl
@@ -0,0 +1,64 @@
+// TestServer.idl : IDL source for TestServer.dll
+//
+
+// This file will be processed by the MIDL tool to
+// produce the type library (TestServer.tlb) and marshalling code.
+
+import "oaidl.idl";
+import "ocidl.idl";
+ [
+ object,
+ uuid(50086EE8-F535-464B-806E-365ADBB727CF),
+ dual,
+ helpstring("ITestServerApp Interface"),
+ pointer_default(unique)
+ ]
+ interface ITestServerApp : IDispatch
+ {
+ [id(1), helpstring("method Test1")] HRESULT Test1([out, retval] ITestServerApp **pVal);
+ [id(2), helpstring("method Test2")] HRESULT Test2([out, retval] VARIANT *pVar);
+ [propget, id(3), helpstring("property MyProp1")] HRESULT MyProp1([out, retval] long *pVal);
+ };
+ [
+ object,
+ uuid(F1A3CC2E-4B2A-4A81-992D-67862076949B),
+ dual,
+ helpstring("IPippo Interface"),
+ pointer_default(unique)
+ ]
+ interface IPippo : IDispatch
+ {
+ [id(1), helpstring("method Method1")] HRESULT Method1([out, retval] IPippo **val);
+ [propget, id(2), helpstring("property MyProp1")] HRESULT MyProp1([out, retval] long *pVal);
+ [id(3), helpstring("method Method2")] HRESULT Method2([in] long in1, [in, out] long *inout1,
+ [out, retval] long *val);
+ };
+
+[
+ uuid(41059C57-975F-4B36-8FF3-C5117426647A),
+ version(1.0),
+ helpstring("TestServer 1.0 Type Library")
+]
+library TESTSERVERLib
+{
+ importlib("stdole32.tlb");
+ importlib("stdole2.tlb");
+ importlib("msado15.dll");
+
+ [
+ uuid(49E44E89-5A72-4456-B1D5-68268A19E798),
+ helpstring("TestServerApp Class")
+ ]
+ coclass TestServerApp
+ {
+ [default] interface ITestServerApp;
+ };
+ [
+ uuid(E19C0A68-F61C-450B-A974-A7BA6957829C),
+ helpstring("Pippo Class")
+ ]
+ coclass Pippo
+ {
+ [default] interface IPippo;
+ };
+};
diff --git a/Lib/site-packages/win32com/test/pippo_server.py b/Lib/site-packages/win32com/test/pippo_server.py
new file mode 100644
index 0000000..c03f3d9
--- /dev/null
+++ b/Lib/site-packages/win32com/test/pippo_server.py
@@ -0,0 +1,80 @@
+# A little test server, complete with typelib, we can use for testing.
+# Originally submitted with bug:
+# [ 753154 ] memory leak wrapping object having _typelib_guid_ attribute
+# but modified by mhammond for use as part of the test suite.
+import sys, os
+import pythoncom
+import win32com
+import winerror
+from win32com.server.util import wrap
+
+class CPippo:
+ #
+ # COM declarations
+ #
+ _reg_clsid_ = "{05AC1CCE-3F9B-4d9a-B0B5-DFE8BE45AFA8}"
+ _reg_desc_ = "Pippo Python test object"
+ _reg_progid_ = "Python.Test.Pippo"
+ #_reg_clsctx_ = pythoncom.CLSCTX_LOCAL_SERVER
+ ###
+ ### Link to typelib
+ _typelib_guid_ = '{41059C57-975F-4B36-8FF3-C5117426647A}'
+ _typelib_version_ = 1, 0
+ _com_interfaces_ = ['IPippo']
+
+ def __init__(self):
+ self.MyProp1 = 10
+
+ def Method1(self):
+ return wrap(CPippo())
+
+ def Method2(self, in1, inout1):
+ return in1, inout1 * 2
+
+def BuildTypelib():
+ from distutils.dep_util import newer
+ this_dir = os.path.dirname(__file__)
+ idl = os.path.abspath(os.path.join(this_dir, "pippo.idl"))
+ tlb=os.path.splitext(idl)[0] + '.tlb'
+ if newer(idl, tlb):
+ print("Compiling %s" % (idl,))
+ rc = os.system ('midl "%s"' % (idl,))
+ if rc:
+ raise RuntimeError("Compiling MIDL failed!")
+ # Can't work out how to prevent MIDL from generating the stubs.
+ # just nuke them
+ for fname in "dlldata.c pippo_i.c pippo_p.c pippo.h".split():
+ os.remove(os.path.join(this_dir, fname))
+
+ print("Registering %s" % (tlb,))
+ tli=pythoncom.LoadTypeLib(tlb)
+ pythoncom.RegisterTypeLib(tli,tlb)
+
+def UnregisterTypelib():
+ k = CPippo
+ try:
+ pythoncom.UnRegisterTypeLib(k._typelib_guid_,
+ k._typelib_version_[0],
+ k._typelib_version_[1],
+ 0,
+ pythoncom.SYS_WIN32)
+ print("Unregistered typelib")
+ except pythoncom.error as details:
+ if details[0]==winerror.TYPE_E_REGISTRYACCESS:
+ pass
+ else:
+ raise
+
+def main(argv=None):
+ if argv is None: argv = sys.argv[1:]
+ if '--unregister' in argv:
+ # Unregister the type-libraries.
+ UnregisterTypelib()
+ else:
+ # Build and register the type-libraries.
+ BuildTypelib()
+ import win32com.server.register
+ win32com.server.register.UseCommandLine(CPippo)
+
+if __name__=='__main__':
+ main(sys.argv)
diff --git a/Lib/site-packages/win32com/test/policySemantics.py b/Lib/site-packages/win32com/test/policySemantics.py
new file mode 100644
index 0000000..d3fa9d6
--- /dev/null
+++ b/Lib/site-packages/win32com/test/policySemantics.py
@@ -0,0 +1,90 @@
+import win32com.server.util
+import win32com.client
+import pythoncom
+import winerror
+import win32com.test.util
+
+import unittest
+
+class Error(Exception):
+ pass
+
+# An object representing a list of numbers
+class PythonSemanticClass:
+ _public_methods_ = ["In"] # DISPIDs are allocated.
+ _dispid_to_func_ = { 10: 'Add', 11:'Remove'} # DISPIDs specified by the object.
+ def __init__(self):
+ self.list = []
+ def _NewEnum(self):
+ return win32com.server.util.NewEnum(self.list)
+ def _value_(self):
+ # should return an array.
+ return self.list
+ def _Evaluate(self):
+ # return the sum
+ return sum(self.list)
+ def In(self, value):
+ return value in self.list
+ def Add(self, value):
+ self.list.append(value)
+ def Remove(self, value):
+ self.list.remove(value)
+
+def DispExTest(ob):
+ if not __debug__: print("WARNING: Tests dressed up as assertions are being skipped!")
+ assert ob.GetDispID("Add", 0)==10, "Policy did not honour the dispid"
+# Not impl
+# assert ob.GetMemberName(10, 0)=="add", "Policy did not give me the correct function for the dispid"
+ assert ob.GetDispID("Remove", 0)==11, "Policy did not honour the dispid"
+ assert ob.GetDispID("In", 0)==1000, "Allocated dispid unexpected value"
+ assert ob.GetDispID("_NewEnum", 0)==pythoncom.DISPID_NEWENUM, "_NewEnum() got unexpected DISPID"
+ dispids = []
+ dispid = -1
+ while 1:
+ try:
+ dispid = ob.GetNextDispID(0, dispid)
+ dispids.append(dispid)
+ except pythoncom.com_error as xxx_todo_changeme:
+ (hr, desc, exc, arg) = xxx_todo_changeme.args
+ assert hr==winerror.S_FALSE, "Bad result at end of enum"
+ break
+ dispids.sort()
+ if dispids != [pythoncom.DISPID_EVALUATE, pythoncom.DISPID_NEWENUM, 10, 11, 1000]:
+ raise Error("Got back the wrong dispids: %s" % dispids)
+
+def SemanticTest(ob):
+ # First just check our object "generally" as expected.
+ ob.Add(1)
+ ob.Add(2)
+ ob.Add(3)
+ # invoke _value_
+ if ob() != (1,2,3):
+ raise Error("Bad result - got %s" % (repr(ob())))
+
+ dispob = ob._oleobj_
+
+ rc = dispob.Invoke(pythoncom.DISPID_EVALUATE, 0, pythoncom.DISPATCH_METHOD|pythoncom.DISPATCH_PROPERTYGET, 1)
+ if rc != 6:
+ raise Error("Evaluate returned %d" % rc)
+
+
+class Tester(win32com.test.util.TestCase):
+ def setUp(self):
+ debug=0
+ import win32com.server.dispatcher
+ if debug:
+ dispatcher=win32com.server.dispatcher.DefaultDebugDispatcher
+ else:
+ dispatcher=None
+ disp = win32com.server.util.wrap(PythonSemanticClass(), useDispatcher=dispatcher)
+ self.ob = win32com.client.Dispatch(disp)
+ def tearDown(self):
+ self.ob = None
+ def testSemantics(self):
+ SemanticTest(self.ob)
+ def testIDispatchEx(self):
+ dispexob = self.ob._oleobj_.QueryInterface(pythoncom.IID_IDispatchEx)
+ DispExTest(dispexob)
+
+if __name__=='__main__':
+ unittest.main()
diff --git a/Lib/site-packages/win32com/test/readme.txt b/Lib/site-packages/win32com/test/readme.txt
new file mode 100644
index 0000000..8f13ccc
--- /dev/null
+++ b/Lib/site-packages/win32com/test/readme.txt
@@ -0,0 +1,18 @@
+COM Test Suite Readme
+---------------------
+
+Running the test suite:
+-----------------------
+* Open a command prompt
+* Change to the "win32com\test" directory.
+* run "testall.py". This will perform level 1 testing.
+ You may specify 1, 2, or 3 on the command line ("testutil 3")
+ to execute more tests.
+
+In general, this should just run the best it can, utilizing what is available
+on the machine. It is likely some tests will refuse to run due to objects not
+being locally available - this is normal.
+
+The win32com source tree has source code to a C++ and VB component used purely
+for testing. You may like to build and register these, particularly if you
+are doing anything related to argument/result handling.
diff --git a/Lib/site-packages/win32com/test/testADOEvents.py b/Lib/site-packages/win32com/test/testADOEvents.py
new file mode 100644
index 0000000..75c2f3a
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testADOEvents.py
@@ -0,0 +1,74 @@
+from win32com.client import Dispatch, DispatchWithEvents, constants
+import pythoncom
+import os
+import time
+
+finished = 0 # Flag for the wait loop from (3) to test
+
+class ADOEvents: # event handler class
+ def OnWillConnect(self, str, user, pw, opt, sts, cn):
+ # Must have this event, as if it is not handled, ADO assumes the
+ # operation is cancelled, and raises an error (Operation cancelled
+ # by the user)
+ pass
+ def OnConnectComplete(self, error, status, connection):
+ # Assume no errors, until we have the basic stuff
+ # working. Now, "connection" should be an open
+ # connection to my data source
+ # Do the "something" from (2). For now, just
+ # print the connection data source
+ print("connection is", connection)
+ print("Connected to", connection.Properties("Data Source"))
+ # OK, our work is done. Let the main loop know
+ global finished
+ finished = 1
+ def OnCommitTransComplete(self, pError, adStatus, pConnection):
+ pass
+ def OnInfoMessage(self, pError, adStatus, pConnection):
+ pass
+ def OnDisconnect(self, adStatus, pConnection):
+ pass
+ def OnBeginTransComplete(self, TransactionLevel, pError, adStatus, pConnection):
+ pass
+ def OnRollbackTransComplete(self, pError, adStatus, pConnection):
+ pass
+ def OnExecuteComplete(self, RecordsAffected, pError, adStatus, pCommand, pRecordset, pConnection):
+ pass
+ def OnWillExecute(self, Source, CursorType, LockType, Options, adStatus, pCommand, pRecordset, pConnection):
+ pass
+
+def TestConnection(dbname):
+ # Create the ADO connection object, and link the event
+ # handlers into it
+ c = DispatchWithEvents("ADODB.Connection", ADOEvents)
+
+ # Initiate the asynchronous open
+ dsn = "Driver={Microsoft Access Driver (*.mdb)};Dbq=%s" % dbname
+ user = "system"
+ pw = "manager"
+ c.Open(dsn, user, pw, constants.adAsyncConnect)
+
+ # Sit in a loop, until our event handler (above) sets the
+ # "finished" flag or we time out.
+ end_time = time.clock() + 10
+ while time.clock() < end_time:
+ # Pump messages so that COM gets a look in
+ pythoncom.PumpWaitingMessages()
+ if not finished:
+ print("XXX - Failed to connect!")
+
+def Test():
+ from . import testAccess
+ try:
+ testAccess.GenerateSupport()
+ except pythoncom.com_error:
+ print("*** Can not import the MSAccess type libraries - tests skipped")
+ return
+ dbname = testAccess.CreateTestAccessDatabase()
+ try:
+ TestConnection(dbname)
+ finally:
+ os.unlink(dbname)
+
+if __name__=='__main__':
+ Test()
diff --git a/Lib/site-packages/win32com/test/testAXScript.py b/Lib/site-packages/win32com/test/testAXScript.py
new file mode 100644
index 0000000..bc480b8
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testAXScript.py
@@ -0,0 +1,34 @@
+# Test AXScripting the best we can in an automated fashion...
+import win32api, os, sys
+
+import win32com.axscript
+import win32com.axscript.client
+
+import unittest
+import win32com.test.util
+
+verbose = "-v" in sys.argv
+
+class AXScript(win32com.test.util.TestCase):
+ def setUp(self):
+ file = win32api.GetFullPathName(os.path.join(win32com.axscript.client.__path__[0], "pyscript.py"))
+ from win32com.test.util import RegisterPythonServer
+ self.verbose = verbose
+ RegisterPythonServer(file, 'python', verbose=self.verbose)
+
+ def testHost(self):
+ file = win32api.GetFullPathName(os.path.join(win32com.axscript.__path__[0], "test\\testHost.py"))
+ cmd = '%s "%s"' % (win32api.GetModuleFileName(0), file)
+ if verbose:
+ print("Testing Python Scripting host")
+ win32com.test.util.ExecuteShellCommand(cmd, self)
+
+ def testCScript(self):
+ file = win32api.GetFullPathName(os.path.join(win32com.axscript.__path__[0], "Demos\\Client\\wsh\\test.pys"))
+ cmd = 'cscript.exe "%s"' % (file)
+ if verbose:
+ print("Testing Windows Scripting host with Python script")
+ win32com.test.util.ExecuteShellCommand(cmd, self)
+
+if __name__=='__main__':
+ win32com.test.util.testmain()
diff --git a/Lib/site-packages/win32com/test/testAccess.py b/Lib/site-packages/win32com/test/testAccess.py
new file mode 100644
index 0000000..0cba558
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testAccess.py
@@ -0,0 +1,169 @@
+#
+# This assumes that you have MSAccess and DAO installed.
+# You need to run makepy.py over "msaccess.tlb" and
+# "dao3032.dll", and ensure the generated files are on the
+# path.
+
+# You can run this with no args, and a test database will be generated.
+# You can optionally pass a dbname on the command line, in which case it will be dumped.
+
+import pythoncom
+from win32com.client import gencache, constants, Dispatch
+import win32api
+import os, sys
+
+def CreateTestAccessDatabase(dbname = None):
+ # Creates a test access database - returns the filename.
+ if dbname is None:
+ dbname = os.path.join( win32api.GetTempPath(), "COMTestSuiteTempDatabase.mdb" )
+
+ access = Dispatch("Access.Application")
+ dbEngine = access.DBEngine
+ workspace = dbEngine.Workspaces(0)
+
+ try:
+ os.unlink(dbname)
+ except os.error:
+ print("WARNING - Unable to delete old test database - expect a COM exception RSN!")
+
+ newdb = workspace.CreateDatabase( dbname, constants.dbLangGeneral, constants.dbEncrypt )
+
+ # Create one test table.
+ table = newdb.CreateTableDef("Test Table 1")
+ table.Fields.Append( table.CreateField("First Name", constants.dbText ) )
+ table.Fields.Append( table.CreateField("Last Name", constants.dbText ) )
+
+ index = table.CreateIndex("UniqueIndex")
+ index.Fields.Append( index.CreateField("First Name") )
+ index.Fields.Append( index.CreateField("Last Name") )
+ index.Unique = -1
+ table.Indexes.Append(index)
+
+ newdb.TableDefs.Append( table )
+
+ # Create a second test table.
+ table = newdb.CreateTableDef("Test Table 2")
+ table.Fields.Append( table.CreateField("First Name", constants.dbText ) )
+ table.Fields.Append( table.CreateField("Last Name", constants.dbText ) )
+
+ newdb.TableDefs.Append( table )
+
+ # Create a relationship between them
+ relation = newdb.CreateRelation("TestRelationship")
+ relation.Table = "Test Table 1"
+ relation.ForeignTable = "Test Table 2"
+
+ field = relation.CreateField("First Name")
+ field.ForeignName = "First Name"
+ relation.Fields.Append( field )
+
+ field = relation.CreateField("Last Name")
+ field.ForeignName = "Last Name"
+ relation.Fields.Append( field )
+
+ relation.Attributes = constants.dbRelationDeleteCascade + constants.dbRelationUpdateCascade
+
+ newdb.Relations.Append(relation)
+
+ # Finally we can add some data to the table.
+ tab1 = newdb.OpenRecordset("Test Table 1")
+ tab1.AddNew()
+ tab1.Fields("First Name").Value = "Mark"
+ tab1.Fields("Last Name").Value = "Hammond"
+ tab1.Update()
+
+ tab1.MoveFirst()
+ # We do a simple bookmark test which tests our optimized VT_SAFEARRAY|VT_UI1 support.
+ # The bookmark will be a buffer object - remember it for later.
+ bk = tab1.Bookmark
+
+ # Add a second record.
+ tab1.AddNew()
+ tab1.Fields("First Name").Value = "Second"
+ tab1.Fields("Last Name").Value = "Person"
+ tab1.Update()
+
+ # Reset the bookmark to the one we saved.
+ # But first check the test is actually doing something!
+ tab1.MoveLast()
+ if tab1.Fields("First Name").Value != "Second":
+ raise RuntimeError("Unexpected record is last - makes bookmark test pointless!")
+
+ tab1.Bookmark = bk
+ if tab1.Bookmark != bk:
+ raise RuntimeError("The bookmark data is not the same")
+
+ if tab1.Fields("First Name").Value != "Mark":
+ raise RuntimeError("The bookmark did not reset the record pointer correctly")
+
+ return dbname
+
+
+def DoDumpAccessInfo(dbname):
+ from . import daodump
+ a = forms = None
+ try:
+ sys.stderr.write("Creating Access Application...\n")
+ a=Dispatch("Access.Application")
+ print("Opening database %s" % dbname)
+ a.OpenCurrentDatabase(dbname)
+ db = a.CurrentDb()
+ daodump.DumpDB(db,1)
+ forms = a.Forms
+ print("There are %d forms open." % (len(forms)))
+# Uncommenting these lines means Access remains open.
+# for form in forms:
+# print " %s" % form.Name
+ reports = a.Reports
+ print("There are %d reports open" % (len(reports)))
+ finally:
+ if not a is None:
+ sys.stderr.write("Closing database\n")
+ try:
+ a.CloseCurrentDatabase()
+ except pythoncom.com_error:
+ pass
+
+# Generate all the support we can.
+def GenerateSupport():
+ # dao
+ gencache.EnsureModule("{00025E01-0000-0000-C000-000000000046}", 0, 4, 0)
+ # Access
+# gencache.EnsureModule("{4AFFC9A0-5F99-101B-AF4E-00AA003F0F07}", 0, 8, 0)
+ gencache.EnsureDispatch("Access.Application")
+
+def DumpAccessInfo(dbname):
+ amod = gencache.GetModuleForProgID("Access.Application")
+ dmod = gencache.GetModuleForProgID("DAO.DBEngine.35")
+ if amod is None and dmod is None:
+ DoDumpAccessInfo(dbname)
+ # Now generate all the support we can.
+ GenerateSupport()
+ else:
+ sys.stderr.write("testAccess not doing dynamic test, as generated code already exists\n")
+ # Now a generated version.
+ DoDumpAccessInfo(dbname)
+
+def test(dbname = None):
+ if dbname is None:
+ # We need makepy support to create a database (just for the constants!)
+ try:
+ GenerateSupport()
+ except pythoncom.com_error:
+ print("*** Can not import the MSAccess type libraries - tests skipped")
+ return
+ dbname = CreateTestAccessDatabase()
+ print("A test database at '%s' was created" % dbname)
+
+ DumpAccessInfo(dbname)
+
+if __name__=='__main__':
+ import sys
+ from .util import CheckClean
+ dbname = None
+ if len(sys.argv)>1:
+ dbname = sys.argv[1]
+
+ test(dbname)
+
+ CheckClean()
diff --git a/Lib/site-packages/win32com/test/testArrays.py b/Lib/site-packages/win32com/test/testArrays.py
new file mode 100644
index 0000000..579f7a1
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testArrays.py
@@ -0,0 +1,124 @@
+# Originally contributed by Stefan Schukat as part of this arbitrary-sized
+# arrays patch.
+from win32com.client import gencache
+from win32com.test import util
+import unittest
+
+ZeroD = 0
+OneDEmpty = []
+OneD = [1,2,3]
+TwoD = [
+ [1,2,3],
+ [1,2,3],
+ [1,2,3]
+ ]
+
+TwoD1 = [
+ [
+ [1,2,3,5],
+ [1,2,3],
+ [1,2,3]
+ ],
+ [
+ [1,2,3],
+ [1,2,3],
+ [1,2,3]
+ ]
+ ]
+
+OneD1 = [
+ [
+ [1,2,3],
+ [1,2,3],
+ [1,2,3]
+ ],
+ [
+ [1,2,3],
+ [1,2,3]
+ ]
+ ]
+
+OneD2 = [
+ [1,2,3],
+ [1,2,3,4,5],
+ [
+ [1,2,3,4,5],
+ [1,2,3,4,5],
+ [1,2,3,4,5]
+ ]
+ ]
+
+
+ThreeD = [
+ [
+ [1,2,3],
+ [1,2,3],
+ [1,2,3]
+ ],
+ [
+ [1,2,3],
+ [1,2,3],
+ [1,2,3]
+ ]
+ ]
+
+FourD = [
+ [
+ [[1,2,3],[1,2,3],[1,2,3]],
+ [[1,2,3],[1,2,3],[1,2,3]],
+ [[1,2,3],[1,2,3],[1,2,3]]
+ ],
+ [
+ [[1,2,3],[1,2,3],[1,2,3]],
+ [[1,2,3],[1,2,3],[1,2,3]],
+ [[1,2,3],[1,2,3],[1,2,3]]
+ ]
+ ]
+
+LargeD = [
+ [ [list(range(10))] * 10],
+] * 512
+
+def _normalize_array(a):
+ if type(a) != type(()):
+ return a
+ ret = []
+ for i in a:
+ ret.append(_normalize_array(i))
+ return ret
+
+class ArrayTest(util.TestCase):
+ def setUp(self):
+ self.arr = gencache.EnsureDispatch("PyCOMTest.ArrayTest")
+ def tearDown(self):
+ self.arr = None
+ def _doTest(self, array):
+ self.arr.Array = array
+ self.failUnlessEqual(_normalize_array(self.arr.Array), array)
+ def testZeroD(self):
+ self._doTest(ZeroD)
+ def testOneDEmpty(self):
+ self._doTest(OneDEmpty)
+ def testOneD(self):
+ self._doTest(OneD)
+ def testTwoD(self):
+ self._doTest(TwoD)
+ def testThreeD(self):
+ self._doTest(ThreeD)
+ def testFourD(self):
+ self._doTest(FourD)
+ def testTwoD1(self):
+ self._doTest(TwoD1)
+ def testOneD1(self):
+ self._doTest(OneD1)
+ def testOneD2(self):
+ self._doTest(OneD2)
+ def testLargeD(self):
+ self._doTest(LargeD)
+
+if __name__ == "__main__":
+ try:
+ util.testmain()
+ except SystemExit as rc:
+ if not rc:
+ raise
diff --git a/Lib/site-packages/win32com/test/testClipboard.py b/Lib/site-packages/win32com/test/testClipboard.py
new file mode 100644
index 0000000..ef3855a
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testClipboard.py
@@ -0,0 +1,160 @@
+# testClipboard.py
+import unittest
+import pythoncom
+import win32con
+import winerror
+import win32clipboard
+
+from pywin32_testutil import str2bytes
+
+from win32com.server.util import NewEnum, wrap
+from win32com.server.exception import COMException
+
+IDataObject_Methods = """GetData GetDataHere QueryGetData
+ GetCanonicalFormatEtc SetData EnumFormatEtc
+ DAdvise DUnadvise EnumDAdvise""".split()
+
+# A COM object implementing IDataObject used for basic testing.
+num_do_objects = 0
+
+def WrapCOMObject(ob, iid=None):
+ return wrap(ob, iid=iid, useDispatcher = 0)
+
+class TestDataObject:
+ _com_interfaces_ = [pythoncom.IID_IDataObject]
+ _public_methods_ = IDataObject_Methods
+ def __init__(self, strval):
+ global num_do_objects
+ num_do_objects += 1
+ self.strval = strval
+ self.supported_fe = []
+ for cf in (win32con.CF_TEXT, win32con.CF_UNICODETEXT):
+ fe = cf, None, pythoncom.DVASPECT_CONTENT, -1, pythoncom.TYMED_HGLOBAL
+ self.supported_fe.append(fe)
+
+ def __del__(self):
+ global num_do_objects
+ num_do_objects -= 1
+
+ def _query_interface_(self, iid):
+ if iid==pythoncom.IID_IEnumFORMATETC:
+ return NewEnum(self.supported_fe, iid=iid)
+
+ def GetData(self, fe):
+ ret_stg = None
+ cf, target, aspect, index, tymed = fe
+ if aspect & pythoncom.DVASPECT_CONTENT and \
+ tymed==pythoncom.TYMED_HGLOBAL:
+ if cf == win32con.CF_TEXT:
+ ret_stg = pythoncom.STGMEDIUM()
+ # ensure always 'bytes' by encoding string.
+ ret_stg.set(pythoncom.TYMED_HGLOBAL, str2bytes(self.strval))
+ elif cf == win32con.CF_UNICODETEXT:
+ ret_stg = pythoncom.STGMEDIUM()
+ ret_stg.set(pythoncom.TYMED_HGLOBAL, str(self.strval))
+
+ if ret_stg is None:
+ raise COMException(hresult=winerror.E_NOTIMPL)
+ return ret_stg
+
+ def GetDataHere(self, fe):
+ raise COMException(hresult=winerror.E_NOTIMPL)
+
+ def QueryGetData(self, fe):
+ cf, target, aspect, index, tymed = fe
+ if aspect & pythoncom.DVASPECT_CONTENT == 0:
+ raise COMException(hresult=winerror.DV_E_DVASPECT)
+ if tymed!=pythoncom.TYMED_HGLOBAL:
+ raise COMException(hresult=winerror.DV_E_TYMED)
+ return None # should check better
+
+ def GetCanonicalFormatEtc(self, fe):
+ RaiseCOMException(winerror.DATA_S_SAMEFORMATETC)
+ # return fe
+
+ def SetData(self, fe, medium):
+ raise COMException(hresult=winerror.E_NOTIMPL)
+
+ def EnumFormatEtc(self, direction):
+ if direction != pythoncom.DATADIR_GET:
+ raise COMException(hresult=winerror.E_NOTIMPL)
+ return NewEnum(self.supported_fe, iid=pythoncom.IID_IEnumFORMATETC)
+
+ def DAdvise(self, fe, flags, sink):
+ raise COMException(hresult=winerror.E_NOTIMPL)
+
+ def DUnadvise(self, connection):
+ raise COMException(hresult=winerror.E_NOTIMPL)
+
+ def EnumDAdvise(self):
+ raise COMException(hresult=winerror.E_NOTIMPL)
+
+class ClipboardTester(unittest.TestCase):
+ def setUp(self):
+ pythoncom.OleInitialize()
+ def tearDown(self):
+ try:
+ pythoncom.OleFlushClipboard()
+ except pythoncom.com_error:
+ # We never set anything!
+ pass
+ def testIsCurrentClipboard(self):
+ do = TestDataObject("Hello from Python")
+ do = WrapCOMObject(do, iid=pythoncom.IID_IDataObject)
+ pythoncom.OleSetClipboard(do)
+ self.failUnless(pythoncom.OleIsCurrentClipboard(do))
+
+ def testComToWin32(self):
+ # Set the data via our DataObject
+ do = TestDataObject("Hello from Python")
+ do = WrapCOMObject(do, iid=pythoncom.IID_IDataObject)
+ pythoncom.OleSetClipboard(do)
+ # Then get it back via the standard win32 clipboard functions.
+ win32clipboard.OpenClipboard()
+ got = win32clipboard.GetClipboardData(win32con.CF_TEXT)
+ # CF_TEXT gives bytes on py3k - use str2bytes() to ensure that's true.
+ expected = str2bytes("Hello from Python")
+ self.assertEqual(got, expected)
+ # Now check unicode
+ got = win32clipboard.GetClipboardData(win32con.CF_UNICODETEXT)
+ self.assertEqual(got, "Hello from Python")
+ win32clipboard.CloseClipboard()
+
+ def testWin32ToCom(self):
+ # Set the data via the std win32 clipboard functions.
+ val = str2bytes("Hello again!") # ensure always bytes, even in py3k
+ win32clipboard.OpenClipboard()
+ win32clipboard.SetClipboardData(win32con.CF_TEXT, val)
+ win32clipboard.CloseClipboard()
+ # and get it via an IDataObject provided by COM
+ do = pythoncom.OleGetClipboard()
+ cf = win32con.CF_TEXT, None, pythoncom.DVASPECT_CONTENT, -1, pythoncom.TYMED_HGLOBAL
+ stg = do.GetData(cf)
+ got = stg.data
+ # The data we get back has the \0, as our STGMEDIUM has no way of
+ # knowing if it meant to be a string, or a binary buffer, so
+ # it must return it too.
+ self.failUnlessEqual(got, str2bytes("Hello again!\0"))
+
+ def testDataObjectFlush(self):
+ do = TestDataObject("Hello from Python")
+ do = WrapCOMObject(do, iid=pythoncom.IID_IDataObject)
+ pythoncom.OleSetClipboard(do)
+ self.assertEqual(num_do_objects, 1)
+
+ do = None # clear my ref!
+ pythoncom.OleFlushClipboard()
+ self.assertEqual(num_do_objects, 0)
+
+ def testDataObjectReset(self):
+ do = TestDataObject("Hello from Python")
+ do = WrapCOMObject(do)
+ pythoncom.OleSetClipboard(do)
+ do = None # clear my ref!
+ self.assertEqual(num_do_objects, 1)
+ pythoncom.OleSetClipboard(None)
+ self.assertEqual(num_do_objects, 0)
+
+if __name__=='__main__':
+ from win32com.test import util
+ util.testmain()
diff --git a/Lib/site-packages/win32com/test/testCollections.py b/Lib/site-packages/win32com/test/testCollections.py
new file mode 100644
index 0000000..483b4f9
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testCollections.py
@@ -0,0 +1,147 @@
+# testCollections.py
+#
+# This code tests both the client and server side of collections
+# and enumerators.
+#
+# Also has the side effect of testing some of the PythonCOM error semantics.
+import sys
+import win32com.server.util
+import win32com.test.util
+import win32com.client
+import traceback
+import pythoncom
+import pywintypes
+import winerror
+L=pywintypes.Unicode
+
+import unittest
+
+error = "collection test error"
+
+def MakeEmptyEnum():
+ # create the Python enumerator object as a real COM object
+ o = win32com.server.util.wrap( win32com.server.util.Collection() )
+ return win32com.client.Dispatch(o)
+
+def MakeTestEnum():
+ # create a sub-collection, just to make sure it works :-)
+ sub = win32com.server.util.wrap( win32com.server.util.Collection( ['Sub1', 2, 'Sub3']) )
+ # create the Python enumerator object as a real COM object
+ o = win32com.server.util.wrap( win32com.server.util.Collection( [1,'Two',3, sub]))
+ return win32com.client.Dispatch(o)
+
+def TestEnumAgainst(o,check):
+ for i in range(len(check)):
+ if o(i) != check[i]:
+ raise error("Using default method gave the incorrect value - %s/%s" % (repr(o(i)), repr(check[i])))
+
+ for i in range(len(check)):
+ if o.Item(i) != check[i]:
+ raise error("Using Item method gave the incorrect value - %s/%s" % (repr(o(i)), repr(check[i])))
+
+ # First try looping.
+ cmp = []
+ for s in o:
+ cmp.append(s)
+
+ if cmp[:len(check)] != check:
+ raise error("Result after looping isnt correct - %s/%s" % (repr(cmp[:len(check)]), repr(check)))
+
+ for i in range(len(check)):
+ if o[i] != check[i]:
+ raise error("Using indexing gave the incorrect value")
+
+
+def TestEnum(quiet=None):
+ if quiet is None:
+ quiet = not "-v" in sys.argv
+ if not quiet: print("Simple enum test")
+ o = MakeTestEnum()
+ check = [1,'Two',3]
+ TestEnumAgainst(o, check)
+
+ if not quiet: print("sub-collection test")
+ sub = o[3]
+ TestEnumAgainst(sub ,['Sub1', 2, 'Sub3'])
+
+ # Remove the sublist for this test!
+ o.Remove(o.Count()-1)
+
+ if not quiet: print("Remove item test")
+ del check[1]
+ o.Remove(1)
+ TestEnumAgainst(o, check)
+
+ if not quiet: print("Add item test")
+ o.Add('New Item')
+ check.append('New Item')
+ TestEnumAgainst(o, check)
+
+ if not quiet: print("Insert item test")
+ o.Insert(2, -1)
+ check.insert(2, -1)
+ TestEnumAgainst(o, check)
+
+### This does not work!
+# if not quiet: print "Indexed replace item test"
+# o[2] = 'Replaced Item'
+# check[2] = 'Replaced Item'
+# TestEnumAgainst(o, check)
+
+ try:
+ o()
+ raise error("default method with no args worked when it shouldnt have!")
+ except pythoncom.com_error as xxx_todo_changeme:
+ (hr, desc, exc, argErr) = xxx_todo_changeme.args
+ if hr != winerror.DISP_E_BADPARAMCOUNT:
+ raise error("Expected DISP_E_BADPARAMCOUNT - got %d (%s)" % (hr, desc))
+
+ try:
+ o.Insert("foo", 2)
+ raise error("Insert worked when it shouldnt have!")
+ except pythoncom.com_error as xxx_todo_changeme1:
+ (hr, desc, exc, argErr) = xxx_todo_changeme1.args
+ if hr != winerror.DISP_E_TYPEMISMATCH:
+ raise error("Expected DISP_E_TYPEMISMATCH - got %d (%s)" % (hr, desc))
+
+ # Remove the sublist for this test!
+ try:
+ o.Remove(o.Count())
+ raise error("Remove worked when it shouldnt have!")
+ except pythoncom.com_error as xxx_todo_changeme2:
+ (hr, desc, exc, argErr) = xxx_todo_changeme2.args
+ if hr != winerror.DISP_E_BADINDEX:
+ raise error("Expected DISP_E_BADINDEX - got %d (%s)" % (hr, desc))
+
+ # Test an empty collection
+ if not quiet: print("Empty collection test")
+ o = MakeEmptyEnum()
+ for item in o:
+ raise error("Empty list performed an iteration")
+
+ try:
+ ob = o[1]
+ raise error("Empty list could be indexed")
+ except IndexError:
+ pass
+
+ try:
+ ob = o[0]
+ raise error("Empty list could be indexed")
+ except IndexError:
+ pass
+
+ try:
+ ob = o(0)
+ raise error("Empty list could be indexed")
+ except pythoncom.com_error as xxx_todo_changeme3:
+ (hr, fn, desc, arg) = xxx_todo_changeme3.args
+ if hr != winerror.DISP_E_BADINDEX:
+ raise error("Expected DISP_E_BADINDEX - got %d (%s)" % (hr, desc))
+
+class TestCase(win32com.test.util.TestCase):
+ def testEnum(self):
+ TestEnum()
+
+if __name__=='__main__':
+ unittest.main()
diff --git a/Lib/site-packages/win32com/test/testDCOM.py b/Lib/site-packages/win32com/test/testDCOM.py
new file mode 100644
index 0000000..7576211
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testDCOM.py
@@ -0,0 +1,40 @@
+# testDCOM
+usage="""\
+testDCOM.py - Simple DCOM test
+Usage: testDCOM.py serverName
+
+Attempts to start the Python.Interpreter object on the named machine,
+and checks that the object is indeed running remotely.
+
+Requires the named server be configured to run DCOM (using dcomcnfg.exe),
+and the Python.Interpreter object installed and registered on that machine.
+
+The Python.Interpreter object must be installed on the local machine,
+but no special DCOM configuration should be necessary.
+"""
+# NOTE: If you configured the object locally using dcomcnfg, you could
+# simple use Dispatch rather than DispatchEx.
+import pythoncom, win32com.client, win32api, string, sys
+
+def test(serverName):
+ if string.lower(serverName)==string.lower(win32api.GetComputerName()):
+ print("You must specify a remote server name, not the local machine!")
+ return
+
+ # Hack to overcome a DCOM limitation. As the Python.Interpreter object
+ # is probably installed locally as an InProc object, DCOM seems to ignore
+ # all settings, and use the local object.
+ clsctx = pythoncom.CLSCTX_SERVER & ~pythoncom.CLSCTX_INPROC_SERVER
+ ob = win32com.client.DispatchEx("Python.Interpreter", serverName, clsctx=clsctx)
+ ob.Exec("import win32api")
+ actualName = ob.Eval("win32api.GetComputerName()")
+ if string.lower(serverName) != string.lower(actualName):
+ print("Error: The object created on server '%s' reported its name as '%s'" % (serverName, actualName))
+ else:
+ print("Object created and tested OK on server '%s'" % serverName)
+
+if __name__=='__main__':
+ if len(sys.argv) == 2:
+ test(sys.argv[1])
+ else:
+ print(usage)
diff --git a/Lib/site-packages/win32com/test/testDictionary.py b/Lib/site-packages/win32com/test/testDictionary.py
new file mode 100644
index 0000000..bc1d865
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testDictionary.py
@@ -0,0 +1,82 @@
+# testDictionary.py
+#
+import sys
+import win32com.server.util
+import win32com.test.util
+import win32com.client
+import traceback
+import pythoncom
+import pywintypes
+import winerror
+
+import unittest
+
+error = "dictionary test error"
+
+def MakeTestDictionary():
+ return win32com.client.Dispatch("Python.Dictionary")
+
+def TestDictAgainst(dict,check):
+ for key, value in check.items():
+ if dict(key) != value:
+ raise error("Indexing for '%s' gave the incorrect value - %s/%s" % (repr(key), repr(dict[key]), repr(check[key])))
+
+# Ensure we have the correct version registered.
+def Register(quiet):
+ import win32com.servers.dictionary
+ from win32com.test.util import RegisterPythonServer
+ RegisterPythonServer(win32com.servers.dictionary.__file__, 'Python.Dictionary')
+
+def TestDict(quiet=None):
+ if quiet is None:
+ quiet = not "-v" in sys.argv
+ Register(quiet)
+
+ if not quiet: print("Simple enum test")
+ dict = MakeTestDictionary()
+ checkDict = {}
+ TestDictAgainst(dict, checkDict)
+
+ dict["NewKey"] = "NewValue"
+ checkDict["NewKey"] = "NewValue"
+ TestDictAgainst(dict, checkDict)
+
+ dict["NewKey"] = None
+ del checkDict["NewKey"]
+ TestDictAgainst(dict, checkDict)
+
+ if not quiet:
+ print("Failure tests")
+ try:
+ dict()
+ raise error("default method with no args worked when it shouldnt have!")
+ except pythoncom.com_error as xxx_todo_changeme:
+ (hr, desc, exc, argErr) = xxx_todo_changeme.args
+ if hr != winerror.DISP_E_BADPARAMCOUNT:
+ raise error("Expected DISP_E_BADPARAMCOUNT - got %d (%s)" % (hr, desc))
+
+ try:
+ dict("hi", "there")
+ raise error("multiple args worked when it shouldnt have!")
+ except pythoncom.com_error as xxx_todo_changeme1:
+ (hr, desc, exc, argErr) = xxx_todo_changeme1.args
+ if hr != winerror.DISP_E_BADPARAMCOUNT:
+ raise error("Expected DISP_E_BADPARAMCOUNT - got %d (%s)" % (hr, desc))
+
+ try:
+ dict(0)
+ raise error("int key worked when it shouldnt have!")
+ except pythoncom.com_error as xxx_todo_changeme2:
+ (hr, desc, exc, argErr) = xxx_todo_changeme2.args
+ if hr != winerror.DISP_E_TYPEMISMATCH:
+ raise error("Expected DISP_E_TYPEMISMATCH - got %d (%s)" % (hr, desc))
+
+ if not quiet:
+ print("Python.Dictionary tests complete.")
+
+class TestCase(win32com.test.util.TestCase):
+ def testDict(self):
+ TestDict()
+
+if __name__=='__main__':
+ unittest.main()
diff --git a/Lib/site-packages/win32com/test/testDictionary.vbs b/Lib/site-packages/win32com/test/testDictionary.vbs
new file mode 100644
index 0000000..9c13053
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testDictionary.vbs
@@ -0,0 +1,26 @@
+' Test Pyhon.Dictionary using VBScript - this uses
+' IDispatchEx, so is an interesting test.
+
+set ob = CreateObject("Python.Dictionary")
+ob("hello") = "there"
+' Our keys are case insensitive.
+ob.Item("hi") = ob("HELLO")
+
+dim ok
+ok = true
+
+if ob("hello") <> "there" then
+ WScript.Echo "**** The dictionary value was wrong!!"
+ ok = false
+end if
+
+if ob("hi") <> "there" then
+ WScript.Echo "**** The other dictionary value was wrong!!"
+ ok = false
+end if
+
+if ok then
+ WScript.Echo "VBScript has successfully tested Python.Dictionary"
+end if
+
+
diff --git a/Lib/site-packages/win32com/test/testDynamic.py b/Lib/site-packages/win32com/test/testDynamic.py
new file mode 100644
index 0000000..4ca776c
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testDynamic.py
@@ -0,0 +1,75 @@
+# Test dynamic policy, and running object table.
+
+import pythoncom
+import winerror
+
+from win32com.server.exception import Exception
+
+error = "testDynamic error"
+
+iid = pythoncom.MakeIID("{b48969a0-784b-11d0-ae71-d23f56000000}")
+
+class VeryPermissive:
+ def _dynamic_(self, name, lcid, wFlags, args):
+ if wFlags & pythoncom.DISPATCH_METHOD:
+ return getattr(self,name)(*args)
+
+ if wFlags & pythoncom.DISPATCH_PROPERTYGET:
+ try:
+ # to avoid problems with byref param handling, tuple results are converted to lists.
+ ret = self.__dict__[name]
+ if type(ret)==type(()):
+ ret = list(ret)
+ return ret
+ except KeyError: # Probably a method request.
+ raise Exception(scode=winerror.DISP_E_MEMBERNOTFOUND)
+
+ if wFlags & (pythoncom.DISPATCH_PROPERTYPUT | pythoncom.DISPATCH_PROPERTYPUTREF):
+ setattr(self, name, args[0])
+ return
+
+ raise Exception(scode=winerror.E_INVALIDARG, desc="invalid wFlags")
+
+ def write(self, *args):
+ if len(args)==0:
+ raise Exception(scode=winerror.DISP_E_BADPARAMCOUNT) # Probably call as PROPGET.
+
+ for arg in args[:-1]:
+ print(str(arg), end=' ')
+ print(str(args[-1]))
+
+def Test():
+ import win32com.server.util, win32com.server.policy
+# import win32dbg;win32dbg.brk()
+ ob = win32com.server.util.wrap(VeryPermissive(),usePolicy=win32com.server.policy.DynamicPolicy)
+ try:
+ handle = pythoncom.RegisterActiveObject(ob, iid, 0)
+ except pythoncom.com_error as details:
+ print("Warning - could not register the object in the ROT:", details)
+ handle = None
+ try:
+ import win32com.client.dynamic
+ client = win32com.client.dynamic.Dispatch(iid)
+ client.ANewAttr = "Hello"
+ if client.ANewAttr != "Hello":
+ raise error("Could not set dynamic property")
+
+ v = ["Hello","From","Python",1.4]
+ client.TestSequence = v
+ if v != list(client.TestSequence):
+ raise error("Dynamic sequences not working! %r/%r" % (repr(v), repr(client.testSequence)))
+
+ client.write("This","output","has","come","via","COM")
+ # Check our new "_FlagAsMethod" works (kinda!)
+ client._FlagAsMethod("NotReallyAMethod")
+ if not callable(client.NotReallyAMethod):
+ raise error("Method I flagged as callable isn't!")
+
+
+ client = None
+ finally:
+ if handle is not None:
+ pythoncom.RevokeActiveObject(handle)
+
+if __name__=='__main__':
+ Test()
diff --git a/Lib/site-packages/win32com/test/testExchange.py b/Lib/site-packages/win32com/test/testExchange.py
new file mode 100644
index 0000000..060817d
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testExchange.py
@@ -0,0 +1,112 @@
+# TestExchange = Exchange Server Dump
+# Note that this code uses "CDO", which is unlikely to get the best choice.
+# You should use the Outlook object model, or
+# the win32com.mapi examples for a low-level interface.
+
+from win32com.client import gencache, constants
+import pythoncom
+import os
+
+ammodule = None # was the generated module!
+
+def GetDefaultProfileName():
+ import win32api, win32con
+ try:
+ key = win32api.RegOpenKey(win32con.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows Messaging Subsystem\\Profiles")
+ try:
+ return win32api.RegQueryValueEx(key, "DefaultProfile")[0]
+ finally:
+ key.Close()
+ except win32api.error:
+ return None
+
+#
+# Recursive dump of folders.
+#
+def DumpFolder(folder, indent = 0):
+ print(" " * indent, folder.Name)
+ folders = folder.Folders
+ folder = folders.GetFirst()
+ while folder:
+ DumpFolder(folder, indent+1)
+ folder = folders.GetNext()
+
+def DumpFolders(session):
+ try:
+ infostores = session.InfoStores
+ except AttributeError:
+ # later outlook?
+ store = session.DefaultStore
+ folder = store.GetRootFolder()
+ DumpFolder(folder)
+ return
+
+ print(infostores)
+ print("There are %d infostores" % infostores.Count)
+ for i in range(infostores.Count):
+ infostore = infostores[i+1]
+ print("Infostore = ", infostore.Name)
+ try:
+ folder = infostore.RootFolder
+ except pythoncom.com_error as details:
+ hr, msg, exc, arg = details
+ # -2147221219 == MAPI_E_FAILONEPROVIDER - a single provider temporarily not available.
+ if exc and exc[-1]==-2147221219:
+ print("This info store is currently not available")
+ continue
+ DumpFolder(folder)
+
+# Build a dictionary of property tags, so I can reverse look-up
+#
+PropTagsById={}
+if ammodule:
+ for name, val in ammodule.constants.__dict__.items():
+ PropTagsById[val] = name
+
+
+def TestAddress(session):
+# entry = session.GetAddressEntry("Skip")
+# print entry
+ pass
+
+
+def TestUser(session):
+ ae = session.CurrentUser
+ fields = getattr(ae, "Fields", [])
+ print("User has %d fields" % len(fields))
+ for f in range(len(fields)):
+ field = fields[f+1]
+ try:
+ id = PropTagsById[field.ID]
+ except KeyError:
+ id = field.ID
+ print("%s/%s=%s" % (field.Name, id, field.Value))
+
+def test():
+ import win32com.client
+ oldcwd = os.getcwd()
+ try:
+ session = gencache.EnsureDispatch("MAPI.Session")
+ try:
+ session.Logon(GetDefaultProfileName())
+ except pythoncom.com_error as details:
+ print("Could not log on to MAPI:", details)
+ return
+ except pythoncom.error:
+ # no mapi.session - let's try outlook
+ app = gencache.EnsureDispatch("Outlook.Application")
+ session = app.Session
+
+ try:
+ TestUser(session)
+ TestAddress(session)
+ DumpFolders(session)
+ finally:
+ session.Logoff()
+ # It appears Exchange will change the cwd on us :(
+ os.chdir(oldcwd)
+
+if __name__=='__main__':
+ from .util import CheckClean
+ test()
+ CheckClean()
diff --git a/Lib/site-packages/win32com/test/testExplorer.py b/Lib/site-packages/win32com/test/testExplorer.py
new file mode 100644
index 0000000..67937b9
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testExplorer.py
@@ -0,0 +1,106 @@
+# testExplorer -
+
+import sys
+import os
+import win32com.client.dynamic
+from win32com.client import Dispatch
+import win32api
+import win32gui
+import win32con
+import winerror
+import glob
+import pythoncom
+import time
+from win32com.test.util import CheckClean
+
+bVisibleEventFired = 0
+
+class ExplorerEvents:
+ def OnVisible(self, visible):
+ global bVisibleEventFired
+ bVisibleEventFired = 1
+
+def TestExplorerEvents():
+ global bVisibleEventFired
+ iexplore = win32com.client.DispatchWithEvents("InternetExplorer.Application", ExplorerEvents)
+ iexplore.Visible = 1
+ if not bVisibleEventFired:
+ raise RuntimeError("The IE event did not appear to fire!")
+ iexplore.Quit()
+ iexplore = None
+
+ bVisibleEventFired = 0
+ ie = win32com.client.Dispatch("InternetExplorer.Application")
+ ie_events = win32com.client.DispatchWithEvents(ie, ExplorerEvents)
+ ie.Visible = 1
+ if not bVisibleEventFired:
+ raise RuntimeError("The IE event did not appear to fire!")
+ ie.Quit()
+ ie = None
+ print("IE Event tests worked.")
+
+def TestObjectFromWindow():
+ # Check we can use ObjectFromLresult to get the COM object from the
+ # HWND - see KB Q249232
+ # Locating the HWND is different than the KB says...
+ hwnd = win32gui.FindWindow('IEFrame', None)
+ for child_class in ['TabWindowClass', 'Shell DocObject View',
+ 'Internet Explorer_Server']:
+ hwnd = win32gui.FindWindowEx(hwnd, 0, child_class, None)
+ # ack - not working for markh on vista with IE8 (or maybe it is the
+ # lack of the 'accessibility' components mentioned in Q249232)
+ # either way - not working!
+ return
+ # But here is the point - once you have an 'Internet Explorer_Server',
+ # you can send a message and use ObjectFromLresult to get it back.
+ msg = win32gui.RegisterWindowMessage("WM_HTML_GETOBJECT")
+ rc, result = win32gui.SendMessageTimeout(hwnd, msg, 0, 0, win32con.SMTO_ABORTIFHUNG, 1000)
+ ob = pythoncom.ObjectFromLresult(result, pythoncom.IID_IDispatch, 0)
+ doc = Dispatch(ob)
+ # just to prove it works, set the background color of the document.
+ for color in "red green blue orange white".split():
+ doc.bgColor = color
+ time.sleep(0.2)
+
+def TestExplorer(iexplore):
+ if not iexplore.Visible: iexplore.Visible = -1
+ iexplore.Navigate(win32api.GetFullPathName('..\\readme.htm'))
+ win32api.Sleep(1000)
+ TestObjectFromWindow()
+ win32api.Sleep(3000)
+ try:
+ iexplore.Quit()
+ except (AttributeError, pythoncom.com_error):
+ # User got sick of waiting :)
+ pass
+
+def TestAll():
+ try:
+ try:
+ iexplore = win32com.client.dynamic.Dispatch("InternetExplorer.Application")
+ TestExplorer(iexplore)
+
+ win32api.Sleep(1000)
+ iexplore = None
+
+ # Test IE events.
+ TestExplorerEvents()
+ # Give IE a chance to shutdown, else it can get upset on fast machines.
+ time.sleep(2)
+
+ # Note that the TextExplorerEvents will force makepy - hence
+ # this gencache is really no longer needed.
+
+ from win32com.client import gencache
+ gencache.EnsureModule("{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}", 0, 1, 1)
+ iexplore = win32com.client.Dispatch("InternetExplorer.Application")
+ TestExplorer(iexplore)
+ except pythoncom.com_error as exc:
+ if exc.hresult!=winerror.RPC_E_DISCONNECTED: # user closed the app!
+ raise
+ finally:
+ iexplore = None
+
+if __name__=='__main__':
+ TestAll()
+ CheckClean()
diff --git a/Lib/site-packages/win32com/test/testGIT.py b/Lib/site-packages/win32com/test/testGIT.py
new file mode 100644
index 0000000..6ec8b02
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testGIT.py
@@ -0,0 +1,119 @@
+"""Testing pasing object between multiple COM threads
+
+Uses standard COM marshalling to pass objects between threads. Even
+though Python generally seems to work when you just pass COM objects
+between threads, it shouldnt.
+
+This shows the "correct" way to do it.
+
+It shows that although we create new threads to use the Python.Interpreter,
+COM marshalls back all calls to that object to the main Python thread,
+which must be running a message loop (as this sample does).
+
+When this test is run in "free threaded" mode (at this stage, you must
+manually mark the COM objects as "ThreadingModel=Free", or run from a
+service which has marked itself as free-threaded), then no marshalling
+is done, and the Python.Interpreter object start doing the "expected" thing
+- ie, it reports being on the same thread as its caller!
+
+Python.exe needs a good way to mark itself as FreeThreaded - at the moment
+this is a pain in the but!
+
+"""
+
+import _thread, traceback
+import win32com.client
+import win32event, win32api
+import pythoncom
+
+def TestInterp(interp):
+ if interp.Eval("1+1") != 2:
+ raise ValueError("The interpreter returned the wrong result.")
+ try:
+ interp.Eval(1+1)
+ raise ValueError("The interpreter did not raise an exception")
+ except pythoncom.com_error as details:
+ import winerror
+ if details[0]!=winerror.DISP_E_TYPEMISMATCH:
+ raise ValueError("The interpreter exception was not winerror.DISP_E_TYPEMISMATCH.")
+
+
+def TestInterpInThread(stopEvent, cookie):
+ try:
+ DoTestInterpInThread(cookie)
+ finally:
+ win32event.SetEvent(stopEvent)
+
+def CreateGIT():
+ return pythoncom.CoCreateInstance(pythoncom.CLSID_StdGlobalInterfaceTable,
+ None,
+ pythoncom.CLSCTX_INPROC,
+ pythoncom.IID_IGlobalInterfaceTable)
+
+def DoTestInterpInThread(cookie):
+ try:
+ pythoncom.CoInitialize()
+ myThread = win32api.GetCurrentThreadId()
+ GIT = CreateGIT()
+
+ interp = GIT.GetInterfaceFromGlobal(cookie, pythoncom.IID_IDispatch)
+ interp = win32com.client.Dispatch(interp)
+
+ TestInterp(interp)
+ interp.Exec("import win32api")
+ print("The test thread id is %d, Python.Interpreter's thread ID is %d" % (myThread, interp.Eval("win32api.GetCurrentThreadId()")))
+ interp = None
+ pythoncom.CoUninitialize()
+ except:
+ traceback.print_exc()
+
+def BeginThreadsSimpleMarshal(numThreads, cookie):
+ """Creates multiple threads using simple (but slower) marshalling.
+
+ Single interpreter object, but a new stream is created per thread.
+
+ Returns the handles the threads will set when complete.
+ """
+ ret = []
+ for i in range(numThreads):
+ hEvent = win32event.CreateEvent(None, 0, 0, None)
+ _thread.start_new(TestInterpInThread, (hEvent, cookie))
+ ret.append(hEvent)
+ return ret
+
+
+def test(fn):
+ print("The main thread is %d" % (win32api.GetCurrentThreadId()))
+ GIT = CreateGIT()
+ interp = win32com.client.Dispatch("Python.Interpreter")
+ cookie = GIT.RegisterInterfaceInGlobal(interp._oleobj_, pythoncom.IID_IDispatch)
+
+ events = fn(4, cookie)
+ numFinished = 0
+ while 1:
+ try:
+ rc = win32event.MsgWaitForMultipleObjects(events, 0, 2000, win32event.QS_ALLINPUT)
+ if rc >= win32event.WAIT_OBJECT_0 and rc < win32event.WAIT_OBJECT_0+len(events):
+ numFinished = numFinished + 1
+ if numFinished >= len(events):
+ break
+ elif rc==win32event.WAIT_OBJECT_0 + len(events): # a message
+ # This is critical - whole apartment model demo will hang.
+ pythoncom.PumpWaitingMessages()
+ else: # Timeout
+ print("Waiting for thread to stop with interfaces=%d, gateways=%d" % (pythoncom._GetInterfaceCount(), pythoncom._GetGatewayCount()))
+ except KeyboardInterrupt:
+ break
+ GIT.RevokeInterfaceFromGlobal(cookie)
+ del interp
+ del GIT
+
+if __name__=='__main__':
+ test(BeginThreadsSimpleMarshal)
+ win32api.Sleep(500)
+ # Doing CoUninit here stop Pythoncom.dll hanging when DLLMain shuts-down the process
+ pythoncom.CoUninitialize()
+ if pythoncom._GetInterfaceCount()!=0 or pythoncom._GetGatewayCount()!=0:
+ print("Done with interfaces=%d, gateways=%d" % (pythoncom._GetInterfaceCount(), pythoncom._GetGatewayCount()))
+ else:
+ print("Done.")
diff --git a/Lib/site-packages/win32com/test/testGatewayAddresses.py b/Lib/site-packages/win32com/test/testGatewayAddresses.py
new file mode 100644
index 0000000..cd58b28
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testGatewayAddresses.py
@@ -0,0 +1,133 @@
+# The purpose of this test is to ensure that the gateways objects
+# do the right thing WRT COM rules about object identity etc.
+
+# Also includes a basic test that we support inheritance correctly in
+# gateway interfaces.
+
+# For our test, we create an object of type IID_IPersistStorage
+# This interface derives from IPersist.
+# Therefore, QI's for IID_IDispatch, IID_IUnknown, IID_IPersist and
+# IID_IPersistStorage should all return the same gateway object.
+#
+# In addition, the interface should only need to declare itself as
+# using the IPersistStorage interface, and as the gateway derives
+# from IPersist, it should automatically be available without declaration.
+#
+# We also create an object of type IID_I??, and perform a QI for it.
+# We then jump through a number of hoops, ensuring that the objects
+# returned by the QIs follow all the rules.
+#
+# Here is Gregs summary of the rules:
+# 1) the set of supported interfaces is static and unchanging
+# 2) symmetric: if you QI an interface for that interface, it succeeds
+# 3) reflexive: if you QI against A for B, the new pointer must succeed
+# for a QI for A
+# 4) transitive: if you QI for B, then QI that for C, then QI'ing A for C
+# must succeed
+#
+#
+# Note that 1) Requires cooperation of the Python programmer. The rule to keep is:
+# "whenever you return an _object_ from _query_interface_(), you must return the
+# same object each time for a given IID. Note that you must return the same
+# _wrapped_ object
+# you
+# The rest are tested here.
+
+
+from win32com.server.util import wrap
+import pythoncom
+from .util import CheckClean
+
+numErrors = 0
+
+# Check that the 2 objects both have identical COM pointers.
+def CheckSameCOMObject(ob1, ob2):
+ addr1 = repr(ob1).split()[6][:-1]
+ addr2 = repr(ob2).split()[6][:-1]
+ return addr1==addr2
+
+# Check that the objects conform to COM identity rules.
+def CheckObjectIdentity(ob1, ob2):
+ u1 = ob1.QueryInterface(pythoncom.IID_IUnknown)
+ u2 = ob2.QueryInterface(pythoncom.IID_IUnknown)
+ return CheckSameCOMObject(u1, u2)
+
+def FailObjectIdentity(ob1, ob2, when):
+ if not CheckObjectIdentity(ob1, ob2):
+ global numErrors
+ numErrors = numErrors + 1
+ print(when, "are not identical (%s, %s)" % (repr(ob1), repr(ob2)))
+
+
+class Dummy:
+ _public_methods_ = [] # We never attempt to make a call on this object.
+ _com_interfaces_ = [pythoncom.IID_IPersistStorage]
+
+class Dummy2:
+ _public_methods_ = [] # We never attempt to make a call on this object.
+ _com_interfaces_ = [pythoncom.IID_IPersistStorage, pythoncom.IID_IExternalConnection]
+
+class DeletgatedDummy:
+ _public_methods_ = []
+
+class Dummy3:
+ _public_methods_ = [] # We never attempt to make a call on this object.
+ _com_interfaces_ = [pythoncom.IID_IPersistStorage]
+ def _query_interface_(self, iid):
+ if iid==pythoncom.IID_IExternalConnection:
+ # This will NEVER work - can only wrap the object once!
+ return wrap(DelegatedDummy())
+
+def TestGatewayInheritance():
+ # By default, wrap() creates and discards a temporary object.
+ # This is not necessary, but just the current implementation of wrap.
+ # As the object is correctly discarded, it doesnt affect this test.
+ o = wrap(Dummy(), pythoncom.IID_IPersistStorage)
+ o2 = o.QueryInterface(pythoncom.IID_IUnknown)
+ FailObjectIdentity(o, o2, "IID_IPersistStorage->IID_IUnknown")
+
+ o3 = o2.QueryInterface(pythoncom.IID_IDispatch)
+
+ FailObjectIdentity(o2, o3, "IID_IUnknown->IID_IDispatch")
+ FailObjectIdentity(o, o3, "IID_IPersistStorage->IID_IDispatch")
+
+ o4 = o3.QueryInterface(pythoncom.IID_IPersistStorage)
+ FailObjectIdentity(o, o4, "IID_IPersistStorage->IID_IPersistStorage(2)")
+ FailObjectIdentity(o2, o4, "IID_IUnknown->IID_IPersistStorage(2)")
+ FailObjectIdentity(o3, o4, "IID_IDispatch->IID_IPersistStorage(2)")
+
+
+ o5 = o4.QueryInterface(pythoncom.IID_IPersist)
+ FailObjectIdentity(o, o5, "IID_IPersistStorage->IID_IPersist")
+ FailObjectIdentity(o2, o5, "IID_IUnknown->IID_IPersist")
+ FailObjectIdentity(o3, o5, "IID_IDispatch->IID_IPersist")
+ FailObjectIdentity(o4, o5, "IID_IPersistStorage(2)->IID_IPersist")
+
+def TestMultiInterface():
+ o = wrap(Dummy2(), pythoncom.IID_IPersistStorage)
+ o2 = o.QueryInterface(pythoncom.IID_IExternalConnection)
+
+ FailObjectIdentity(o, o2, "IID_IPersistStorage->IID_IExternalConnection")
+
+ # Make the same QI again, to make sure it is stable.
+ o22 = o.QueryInterface(pythoncom.IID_IExternalConnection)
+ FailObjectIdentity(o, o22, "IID_IPersistStorage->IID_IExternalConnection")
+ FailObjectIdentity(o2, o22, "IID_IPersistStorage->IID_IExternalConnection (stability)")
+
+ o3 = o2.QueryInterface(pythoncom.IID_IPersistStorage)
+ FailObjectIdentity(o2, o3, "IID_IExternalConnection->IID_IPersistStorage")
+ FailObjectIdentity(o, o3, "IID_IPersistStorage->IID_IExternalConnection->IID_IPersistStorage")
+
+
+def test():
+ TestGatewayInheritance()
+ TestMultiInterface()
+ if numErrors==0:
+ print("Worked ok")
+ else:
+ print("There were", numErrors, "errors.")
+
+
+if __name__=='__main__':
+ test()
+ CheckClean()
diff --git a/Lib/site-packages/win32com/test/testInterp.vbs b/Lib/site-packages/win32com/test/testInterp.vbs
new file mode 100644
index 0000000..3060cee
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testInterp.vbs
@@ -0,0 +1,12 @@
+set o = CreateObject("Python.Interpreter")
+if o.Eval("1+1") <> 2 Then
+ WScript.Echo "Eval('1+1') failed"
+ bFailed = True
+end if
+
+if bFailed then
+ WScript.Echo "*********** VBScript tests failed *********"
+else
+ WScript.Echo "VBScript test worked OK"
+end if
+
diff --git a/Lib/site-packages/win32com/test/testIterators.py b/Lib/site-packages/win32com/test/testIterators.py
new file mode 100644
index 0000000..7dfb11d
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testIterators.py
@@ -0,0 +1,132 @@
+from __future__ import generators
+
+# Some raw iter tests. Some "high-level" iterator tests can be found in
+# testvb.py and testOutlook.py
+import sys
+import unittest
+
+from win32com.client.gencache import EnsureDispatch
+from win32com.client import Dispatch
+import win32com.server.util
+import win32com.test.util
+import pythoncom
+
+def yield_iter(iter):
+ while 1:
+ yield next(iter)
+
+class _BaseTestCase(win32com.test.util.TestCase):
+ def test_enumvariant_vb(self):
+ ob, iter = self.iter_factory()
+ got=[]
+ for v in iter:
+ got.append(v)
+ self.assertEquals(got, self.expected_data)
+ def test_yield(self):
+ ob, i = self.iter_factory()
+ got=[]
+ for v in yield_iter(iter(i)):
+ got.append(v)
+ self.assertEquals(got, self.expected_data)
+
+ def _do_test_nonenum(self, object):
+ try:
+ for i in object:
+ pass
+ self.fail("Could iterate over a non-iterable object")
+ except TypeError:
+ pass # this is expected.
+ self.assertRaises(TypeError, iter, object)
+ self.assertRaises(AttributeError, getattr, object, "next")
+
+ def test_nonenum_wrapper(self):
+ # Check our raw PyIDispatch
+ ob = self.object._oleobj_
+ try:
+ for i in ob:
+ pass
+ self.fail("Could iterate over a non-iterable object")
+ except TypeError:
+ pass # this is expected.
+ self.assertRaises(TypeError, iter, ob)
+ self.assertRaises(AttributeError, getattr, ob, "next")
+
+ # And our Dispatch wrapper
+ ob = self.object
+ try:
+ for i in ob:
+ pass
+ self.fail("Could iterate over a non-iterable object")
+ except TypeError:
+ pass # this is expected.
+ # Note that as our object may be dynamic, we *do* have a __getitem__
+ # method, meaning we *can* call iter() on the object. In this case
+ # actual iteration is what fails.
+ # So either the 'iter(); will raise a type error, or an attempt to
+ # fetch it
+ try:
+ next(iter(ob))
+ self.fail("Expected a TypeError fetching this iterator")
+ except TypeError:
+ pass
+ # And it should never have a 'next' method
+ self.assertRaises(AttributeError, getattr, ob, "next")
+
+class VBTestCase(_BaseTestCase):
+ def setUp(self):
+ def factory():
+ # Our VB test harness exposes a property with IEnumVariant.
+ ob = self.object.EnumerableCollectionProperty
+ for i in self.expected_data:
+ ob.Add(i)
+ # Get the raw IEnumVARIANT.
+ invkind = pythoncom.DISPATCH_METHOD | pythoncom.DISPATCH_PROPERTYGET
+ iter = ob._oleobj_.InvokeTypes(pythoncom.DISPID_NEWENUM,0,invkind,(13, 10),())
+ return ob, iter.QueryInterface(pythoncom.IID_IEnumVARIANT)
+ # We *need* generated dispatch semantics, so dynamic __getitem__ etc
+ # don't get in the way of our tests.
+ self.object = EnsureDispatch("PyCOMVBTest.Tester")
+ self.expected_data = [1, "Two", "3"]
+ self.iter_factory = factory
+
+ def tearDown(self):
+ self.object = None
+
+# Test our client semantics, but using a wrapped Python list object.
+# This has the effect of re-using our client specific tests, but in this
+# case is exercising the server side.
+class SomeObject:
+ _public_methods_ = ["GetCollection"]
+ def __init__(self, data):
+ self.data = data
+ def GetCollection(self):
+ return win32com.server.util.NewCollection(self.data)
+
+class WrappedPythonCOMServerTestCase(_BaseTestCase):
+ def setUp(self):
+ def factory():
+ ob = self.object.GetCollection()
+ flags = pythoncom.DISPATCH_METHOD | pythoncom.DISPATCH_PROPERTYGET
+ enum = ob._oleobj_.Invoke(pythoncom.DISPID_NEWENUM, 0, flags, 1)
+ return ob, enum.QueryInterface(pythoncom.IID_IEnumVARIANT)
+
+ self.expected_data = [1,'Two',3]
+ sv = win32com.server.util.wrap(SomeObject(self.expected_data))
+ self.object = Dispatch(sv)
+ self.iter_factory = factory
+
+ def tearDown(self):
+ self.object = None
+
+def suite():
+ # We dont want our base class run
+ suite = unittest.TestSuite()
+ for item in globals().values():
+ if type(item)==type(unittest.TestCase) and \
+ issubclass(item, unittest.TestCase) and \
+ item != _BaseTestCase:
+ suite.addTest(unittest.makeSuite(item))
+ return suite
+
+if __name__=='__main__':
+ unittest.main(argv=sys.argv + ['suite'])
diff --git a/Lib/site-packages/win32com/test/testMSOffice.py b/Lib/site-packages/win32com/test/testMSOffice.py
new file mode 100644
index 0000000..edf3c05
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testMSOffice.py
@@ -0,0 +1,163 @@
+# Test MSOffice
+#
+# Main purpose of test is to ensure that Dynamic COM objects
+# work as expected.
+
+# Assumes Word and Excel installed on your machine.
+
+import win32com, sys, string, win32api, traceback
+import win32com.client.dynamic
+from win32com.test.util import CheckClean
+import pythoncom
+from win32com.client import gencache
+from pywintypes import Unicode
+
+error = "MSOffice test error"
+
+# Test a few of the MSOffice components.
+def TestWord():
+ # Try and load the object exposed by Word 8
+ # Office 97 - _totally_ different object model!
+ try:
+ # NOTE - using "client.Dispatch" would return an msword8.py instance!
+ print("Starting Word 8 for dynamic test")
+ word = win32com.client.dynamic.Dispatch("Word.Application")
+ TestWord8(word)
+
+ word = None
+ # Now we will test Dispatch without the new "lazy" capabilities
+ print("Starting Word 8 for non-lazy dynamic test")
+ dispatch = win32com.client.dynamic._GetGoodDispatch("Word.Application")
+ typeinfo = dispatch.GetTypeInfo()
+ attr = typeinfo.GetTypeAttr()
+ olerepr = win32com.client.build.DispatchItem(typeinfo, attr, None, 0)
+ word = win32com.client.dynamic.CDispatch(dispatch, olerepr)
+ dispatch = typeinfo = attr = olerepr = None
+ TestWord8(word)
+
+ except pythoncom.com_error:
+ print("Starting Word 7 for dynamic test")
+ word = win32com.client.Dispatch("Word.Basic")
+ TestWord7(word)
+
+ print("Starting MSWord for generated test")
+ from win32com.client import gencache
+ word = gencache.EnsureDispatch("Word.Application.8")
+ TestWord8(word)
+
+def TestWord7(word):
+ word.FileNew()
+ # If not shown, show the app.
+ if not word.AppShow(): word._proc_("AppShow")
+
+ for i in range(12):
+ word.FormatFont(Color=i+1, Points=i+12)
+ word.Insert("Hello from Python %d\n" % i)
+
+ word.FileClose(2)
+
+def TestWord8(word):
+ word.Visible = 1
+ doc = word.Documents.Add()
+ wrange = doc.Range()
+ for i in range(10):
+ wrange.InsertAfter("Hello from Python %d\n" % i)
+ paras = doc.Paragraphs
+ for i in range(len(paras)):
+ p = paras[i]()
+ p.Font.ColorIndex = i+1
+ p.Font.Size = 12 + (4 * i)
+ # XXX - note that
+ # for para in paras:
+ # para().Font...
+ # doesnt seem to work - no error, just doesnt work
+ # Should check if it works for VB!
+ doc.Close(SaveChanges = 0)
+ word.Quit()
+ win32api.Sleep(1000) # Wait for word to close, else we
+ # may get OA error.
+
+def TestWord8OldStyle():
+ try:
+ import win32com.test.Generated4Test.msword8
+ except ImportError:
+ print("Can not do old style test")
+
+
+def TextExcel(xl):
+ xl.Visible = 0
+ if xl.Visible: raise error("Visible property is true.")
+ xl.Visible = 1
+ if not xl.Visible: raise error("Visible property not true.")
+
+ if int(xl.Version[0])>=8:
+ xl.Workbooks.Add()
+ else:
+ xl.Workbooks().Add()
+
+
+ xl.Range("A1:C1").Value = (1,2,3)
+ xl.Range("A2:C2").Value = ('x','y','z')
+ xl.Range("A3:C3").Value = ('3','2','1')
+
+ for i in range(20):
+ xl.Cells(i+1,i+1).Value = "Hi %d" % i
+
+ if xl.Range("A1").Value != "Hi 0":
+ raise error("Single cell range failed")
+
+ if xl.Range("A1:B1").Value != ((Unicode("Hi 0"),2),):
+ raise error("flat-horizontal cell range failed")
+
+ if xl.Range("A1:A2").Value != ((Unicode("Hi 0"),),(Unicode("x"),)):
+ raise error("flat-vertical cell range failed")
+
+ if xl.Range("A1:C3").Value != ((Unicode("Hi 0"),2,3),(Unicode("x"),Unicode("Hi 1"),Unicode("z")),(3,2,Unicode("Hi 2"))):
+ raise error("square cell range failed")
+
+ xl.Range("A1:C3").Value =((3,2,1),("x","y","z"),(1,2,3))
+
+ if xl.Range("A1:C3").Value != ((3,2,1),(Unicode("x"),Unicode("y"),Unicode("z")),(1,2,3)):
+ raise error("Range was not what I set it to!")
+
+ # test dates out with Excel
+ xl.Cells(5,1).Value = "Excel time"
+ xl.Cells(5,2).Formula = "=Now()"
+
+ import time
+ xl.Cells(6,1).Value = "Python time"
+ xl.Cells(6,2).Value = pythoncom.MakeTime(time.time())
+ xl.Cells(6,2).NumberFormat = "d/mm/yy h:mm"
+ xl.Columns("A:B").EntireColumn.AutoFit()
+
+ xl.Workbooks(1).Close(0)
+ xl.Quit()
+
+def TestAll():
+ TestWord()
+
+ print("Starting Excel for Dynamic test...")
+ xl = win32com.client.dynamic.Dispatch("Excel.Application")
+ TextExcel(xl)
+
+ try:
+ print("Starting Excel 8 for generated excel8.py test...")
+ mod = gencache.EnsureModule("{00020813-0000-0000-C000-000000000046}", 0, 1, 2, bForDemand=1)
+ xl = win32com.client.Dispatch("Excel.Application")
+ TextExcel(xl)
+ except ImportError:
+ print("Could not import the generated Excel 97 wrapper")
+
+ try:
+ import xl5en32
+ mod = gencache.EnsureModule("{00020813-0000-0000-C000-000000000046}", 9, 1, 0)
+ xl = win32com.client.Dispatch("Excel.Application.5")
+ print("Starting Excel 95 for makepy test...")
+ TextExcel(xl)
+ except ImportError:
+ print("Could not import the generated Excel 95 wrapper")
+
+if __name__=='__main__':
+ TestAll()
+ CheckClean()
+ pythoncom.CoUninitialize()
diff --git a/Lib/site-packages/win32com/test/testMSOfficeEvents.py b/Lib/site-packages/win32com/test/testMSOfficeEvents.py
new file mode 100644
index 0000000..0bb622a
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testMSOfficeEvents.py
@@ -0,0 +1,115 @@
+# OfficeEvents - test/demonstrate events with Word and Excel.
+from win32com.client import DispatchWithEvents, Dispatch
+import msvcrt, pythoncom
+import time, sys
+import types
+
+import threading
+stopEvent = threading.Event()
+
+def TestExcel():
+ class ExcelEvents:
+ def OnNewWorkbook(self, wb):
+ if type(wb) != types.InstanceType:
+ raise RuntimeError("The transformer doesnt appear to have translated this for us!")
+ self.seen_events["OnNewWorkbook"] = None
+ def OnWindowActivate(self, wb, wn):
+ if type(wb) != types.InstanceType or type(wn) != types.InstanceType:
+ raise RuntimeError("The transformer doesnt appear to have translated this for us!")
+ self.seen_events["OnWindowActivate"] = None
+ def OnWindowDeactivate(self, wb, wn):
+ self.seen_events["OnWindowDeactivate"] = None
+ def OnSheetDeactivate(self, sh):
+ self.seen_events["OnSheetDeactivate"] = None
+ def OnSheetBeforeDoubleClick(self, Sh, Target, Cancel):
+ if Target.Column % 2 == 0:
+ print("You can double-click there...")
+ else:
+ print("You can not double-click there...")
+ # This function is a void, so the result ends up in
+ # the only ByRef - Cancel.
+ return 1
+
+ class WorkbookEvents:
+ def OnActivate(self):
+ print("workbook OnActivate")
+ def OnBeforeRightClick(self, Target, Cancel):
+ print("It's a Worksheet Event")
+
+ e = DispatchWithEvents("Excel.Application", ExcelEvents)
+ e.seen_events = {}
+ e.Visible=1
+ book = e.Workbooks.Add()
+ book = DispatchWithEvents(book, WorkbookEvents)
+ print("Have book", book)
+# sheet = e.Worksheets(1)
+# sheet = DispatchWithEvents(sheet, WorksheetEvents)
+
+ print("Double-click in a few of the Excel cells...")
+ print("Press any key when finished with Excel, or wait 10 seconds...")
+ if not _WaitForFinish(e, 10):
+ e.Quit()
+ if not _CheckSeenEvents(e, ["OnNewWorkbook", "OnWindowActivate"]):
+ sys.exit(1)
+
+def TestWord():
+ class WordEvents:
+ def OnDocumentChange(self):
+ self.seen_events["OnDocumentChange"] = None
+ def OnWindowActivate(self, doc, wn):
+ self.seen_events["OnWindowActivate"] = None
+ def OnQuit(self):
+ self.seen_events["OnQuit"] = None
+ stopEvent.set()
+
+ w = DispatchWithEvents("Word.Application", WordEvents)
+ w.seen_events = {}
+ w.Visible = 1
+ w.Documents.Add()
+ print("Press any key when finished with Word, or wait 10 seconds...")
+ if not _WaitForFinish(w, 10):
+ w.Quit()
+ if not _CheckSeenEvents(w, ["OnDocumentChange", "OnWindowActivate"]):
+ sys.exit(1)
+
+def _WaitForFinish(ob, timeout):
+ end = time.time() + timeout
+ while 1:
+ if msvcrt.kbhit():
+ msvcrt.getch()
+ break
+ pythoncom.PumpWaitingMessages()
+ stopEvent.wait(.2)
+ if stopEvent.isSet():
+ stopEvent.clear()
+ break
+ try:
+ if not ob.Visible:
+ # Gone invisible - we need to pretend we timed
+ # out, so the app is quit.
+ return 0
+ except pythoncom.com_error:
+ # Excel is busy (eg, editing the cell) - ignore
+ pass
+ if time.time() > end:
+ return 0
+ return 1
+
+def _CheckSeenEvents(o, events):
+ rc = 1
+ for e in events:
+ if e not in o.seen_events:
+ print("ERROR: Expected event did not trigger", e)
+ rc = 0
+ return rc
+
+def test():
+ import sys
+ if "noword" not in sys.argv[1:]:
+ TestWord()
+ if "noexcel" not in sys.argv[1:]:
+ TestExcel()
+ print("Word and Excel event tests passed.")
+
+if __name__=='__main__':
+ test()
diff --git a/Lib/site-packages/win32com/test/testMarshal.py b/Lib/site-packages/win32com/test/testMarshal.py
new file mode 100644
index 0000000..6cef0ce
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testMarshal.py
@@ -0,0 +1,140 @@
+"""Testing pasing object between multiple COM threads
+
+Uses standard COM marshalling to pass objects between threads. Even
+though Python generally seems to work when you just pass COM objects
+between threads, it shouldnt.
+
+This shows the "correct" way to do it.
+
+It shows that although we create new threads to use the Python.Interpreter,
+COM marshalls back all calls to that object to the main Python thread,
+which must be running a message loop (as this sample does).
+
+When this test is run in "free threaded" mode (at this stage, you must
+manually mark the COM objects as "ThreadingModel=Free", or run from a
+service which has marked itself as free-threaded), then no marshalling
+is done, and the Python.Interpreter object start doing the "expected" thing
+- ie, it reports being on the same thread as its caller!
+
+Python.exe needs a good way to mark itself as FreeThreaded - at the moment
+this is a pain in the but!
+
+"""
+
+import threading, traceback
+import win32com.client
+import win32event, win32api
+import pythoncom
+import unittest
+
+from .testServers import InterpCase
+
+freeThreaded = 1
+
+class ThreadInterpCase(InterpCase):
+ def _testInterpInThread(self, stopEvent, interp):
+ try:
+ self._doTestInThread(interp)
+ finally:
+ win32event.SetEvent(stopEvent)
+
+ def _doTestInThread(self, interp):
+ pythoncom.CoInitialize()
+ myThread = win32api.GetCurrentThreadId()
+
+ if freeThreaded:
+ interp = pythoncom.CoGetInterfaceAndReleaseStream(interp, pythoncom.IID_IDispatch)
+ interp = win32com.client.Dispatch(interp)
+
+ interp.Exec("import win32api")
+ #print "The test thread id is %d, Python.Interpreter's thread ID is %d" % (myThread, interp.Eval("win32api.GetCurrentThreadId()"))
+ pythoncom.CoUninitialize()
+
+ def BeginThreadsSimpleMarshal(self, numThreads):
+ """Creates multiple threads using simple (but slower) marshalling.
+
+ Single interpreter object, but a new stream is created per thread.
+
+ Returns the handles the threads will set when complete.
+ """
+ interp = win32com.client.Dispatch("Python.Interpreter")
+ events = []
+ threads = []
+ for i in range(numThreads):
+ hEvent = win32event.CreateEvent(None, 0, 0, None)
+ events.append(hEvent)
+ interpStream = pythoncom.CoMarshalInterThreadInterfaceInStream(pythoncom.IID_IDispatch, interp._oleobj_)
+ t = threading.Thread(target=self._testInterpInThread, args=(hEvent, interpStream))
+ t.setDaemon(1) # so errors dont cause shutdown hang
+ t.start()
+ threads.append(t)
+ interp = None
+ return threads, events
+
+ #
+ # NOTE - this doesnt quite work - Im not even sure it should, but Greg reckons
+ # you should be able to avoid the marshal per thread!
+ # I think that refers to CoMarshalInterface though...
+ def BeginThreadsFastMarshal(self, numThreads):
+ """Creates multiple threads using fast (but complex) marshalling.
+
+ The marshal stream is created once, and each thread uses the same stream
+
+ Returns the handles the threads will set when complete.
+ """
+ interp = win32com.client.Dispatch("Python.Interpreter")
+ if freeThreaded:
+ interp = pythoncom.CoMarshalInterThreadInterfaceInStream(pythoncom.IID_IDispatch, interp._oleobj_)
+ events = []
+ threads = []
+ for i in range(numThreads):
+ hEvent = win32event.CreateEvent(None, 0, 0, None)
+ t = threading.Thread(target=self._testInterpInThread, args=(hEvent, interp))
+ t.setDaemon(1) # so errors dont cause shutdown hang
+ t.start()
+ events.append(hEvent)
+ threads.append(t)
+ return threads, events
+
+ def _DoTestMarshal(self, fn, bCoWait = 0):
+ #print "The main thread is %d" % (win32api.GetCurrentThreadId())
+ threads, events = fn(2)
+ numFinished = 0
+ while 1:
+ try:
+ if bCoWait:
+ rc = pythoncom.CoWaitForMultipleHandles(0, 2000, events)
+ else:
+ # Specifying "bWaitAll" here will wait for messages *and* all events
+ # (which is pretty useless)
+ rc = win32event.MsgWaitForMultipleObjects(events, 0, 2000, win32event.QS_ALLINPUT)
+ if rc >= win32event.WAIT_OBJECT_0 and rc < win32event.WAIT_OBJECT_0+len(events):
+ numFinished = numFinished + 1
+ if numFinished >= len(events):
+ break
+ elif rc==win32event.WAIT_OBJECT_0 + len(events): # a message
+ # This is critical - whole apartment model demo will hang.
+ pythoncom.PumpWaitingMessages()
+ else: # Timeout
+ print("Waiting for thread to stop with interfaces=%d, gateways=%d" % (pythoncom._GetInterfaceCount(), pythoncom._GetGatewayCount()))
+ except KeyboardInterrupt:
+ break
+ for t in threads:
+ t.join(2)
+ self.failIf(t.isAlive(), "thread failed to stop!?")
+ threads = None # threads hold references to args
+ # Seems to be a leak here I can't locate :(
+ #self.failUnlessEqual(pythoncom._GetInterfaceCount(), 0)
+ #self.failUnlessEqual(pythoncom._GetGatewayCount(), 0)
+
+ def testSimpleMarshal(self):
+ self._DoTestMarshal(self.BeginThreadsSimpleMarshal)
+
+ def testSimpleMarshalCoWait(self):
+ self._DoTestMarshal(self.BeginThreadsSimpleMarshal, 1)
+
+# def testFastMarshal(self):
+# self._DoTestMarshal(self.BeginThreadsFastMarshal)
+
+if __name__=='__main__':
+ unittest.main('testMarshal')
diff --git a/Lib/site-packages/win32com/test/testNetscape.py b/Lib/site-packages/win32com/test/testNetscape.py
new file mode 100644
index 0000000..a81b659
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testNetscape.py
@@ -0,0 +1,25 @@
+## AHH - I cant make this work!!!
+
+# But this is the general idea.
+
+import netscape
+import sys
+
+error = "Netscape Test Error"
+
+if __name__=='__main__':
+ n=netscape.CNetworkCX()
+ rc = n.Open("http://d|/temp/apyext.html", 0, None, 0, None)
+ if not rc: raise error("Open method of Netscape failed")
+ while 1:
+ num, str = n.Read(None, 0)
+ print("Got ", num, str)
+ if num==0:
+ break # used to be continue - no idea!!
+ if num==-1:
+ break
+# sys.stdout.write(str)
+ n.Close()
+ print("Done!")
+ del n
+ sys.last_type = sys.last_value = sys.last_traceback = None
diff --git a/Lib/site-packages/win32com/test/testPersist.py b/Lib/site-packages/win32com/test/testPersist.py
new file mode 100644
index 0000000..de483f1
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testPersist.py
@@ -0,0 +1,212 @@
+import pythoncom
+import win32com.server.util
+import time
+
+import win32com, sys, string, win32api, traceback
+import win32com.client.dynamic
+import win32com.client
+import pythoncom
+from win32com.axcontrol import axcontrol
+from pywintypes import Unicode
+from win32com import storagecon
+from win32com.test.util import CheckClean
+
+import pywintypes
+import win32ui
+import win32api, os
+
+from pywin32_testutil import str2bytes
+
+S_OK = 0
+
+import datetime
+if issubclass(pywintypes.TimeType, datetime.datetime):
+ import win32timezone
+ now = win32timezone.now()
+else:
+ now = pywintypes.Time(time.time())
+
+class LockBytes:
+ _public_methods_ = [ 'ReadAt', 'WriteAt', 'Flush', 'SetSize', 'LockRegion', 'UnlockRegion', 'Stat' ]
+ _com_interfaces_ = [ pythoncom.IID_ILockBytes ]
+
+ def __init__(self, data = ""):
+ self.data = str2bytes(data)
+ self.ctime = now
+ self.mtime = now
+ self.atime = now
+
+ def ReadAt(self, offset, cb):
+ print("ReadAt")
+ result = self.data[offset:offset + cb]
+ return result
+
+ def WriteAt(self, offset, data):
+ print("WriteAt " +str(offset))
+ print("len " + str(len(data)))
+ print("data:")
+ #print data
+ if len(self.data) >= offset:
+ newdata = self.data[0:offset] + data
+ print(len(newdata))
+ if len(self.data) >= offset + len(data):
+ newdata = newdata + self.data[offset + len(data):]
+ print(len(newdata))
+ self.data = newdata
+ return len(data)
+
+ def Flush(self, whatsthis=0):
+ print("Flush" + str(whatsthis))
+ fname = os.path.join(win32api.GetTempPath(), "persist.doc")
+ open(fname, "wb").write(self.data)
+ return S_OK
+
+ def SetSize(self, size):
+ print("Set Size" + str(size))
+ if size > len(self.data):
+ self.data = self.data + str2bytes("\000" * (size - len(self.data)))
+ else:
+ self.data = self.data[0:size]
+ return S_OK
+
+ def LockRegion(self, offset, size, locktype):
+ print("LockRegion")
+ pass
+
+ def UnlockRegion(self, offset, size, locktype):
+ print("UnlockRegion")
+ pass
+
+ def Stat(self, statflag):
+ print("returning Stat " + str(statflag))
+ return (
+ "PyMemBytes",
+ storagecon.STGTY_LOCKBYTES,
+ len(self.data),
+ self.mtime,
+ self.ctime,
+ self.atime,
+ storagecon.STGM_DIRECT|storagecon.STGM_READWRITE|storagecon.STGM_CREATE ,
+ storagecon.STGM_SHARE_EXCLUSIVE,
+ "{00020905-0000-0000-C000-000000000046}",
+ 0, # statebits ?
+ 0
+ )
+
+
+class OleClientSite:
+ _public_methods_ = [ 'SaveObject', 'GetMoniker', 'GetContainer', 'ShowObject', 'OnShowWindow', 'RequestNewObjectLayout' ]
+ _com_interfaces_ = [ axcontrol.IID_IOleClientSite ]
+
+ def __init__(self, data = ""):
+ self.IPersistStorage = None
+ self.IStorage = None
+
+ def SetIPersistStorage(self, IPersistStorage):
+ self.IPersistStorage = IPersistStorage
+
+ def SetIStorage(self, IStorage):
+ self.IStorage = IStorage
+
+ def SaveObject(self):
+ print("SaveObject")
+ if self.IPersistStorage != None and self.IStorage != None:
+ self.IPersistStorage.Save(self.IStorage,1)
+ self.IStorage.Commit(0)
+ return S_OK
+
+ def GetMoniker(self, dwAssign, dwWhichMoniker):
+ print("GetMoniker " + str(dwAssign) + " " + str(dwWhichMoniker))
+
+ def GetContainer(self):
+ print("GetContainer")
+
+ def ShowObject(self):
+ print("ShowObject")
+
+ def OnShowWindow(self, fShow):
+ print("ShowObject" + str(fShow))
+
+ def RequestNewObjectLayout(self):
+ print("RequestNewObjectLayout")
+
+
+def test():
+ # create a LockBytes object and
+ #wrap it as a COM object
+# import win32com.server.dispatcher
+ lbcom = win32com.server.util.wrap(LockBytes(), pythoncom.IID_ILockBytes) #, useDispatcher=win32com.server.dispatcher.DispatcherWin32trace)
+
+ # create a structured storage on the ILockBytes object
+ stcom = pythoncom.StgCreateDocfileOnILockBytes(lbcom, storagecon.STGM_DIRECT| storagecon.STGM_CREATE | storagecon.STGM_READWRITE | storagecon.STGM_SHARE_EXCLUSIVE, 0)
+
+ # create our ClientSite
+ ocs = OleClientSite()
+ # wrap it as a COM object
+ ocscom = win32com.server.util.wrap(ocs, axcontrol.IID_IOleClientSite)
+
+ # create a Word OLE Document, connect it to our site and our storage
+ oocom=axcontrol.OleCreate("{00020906-0000-0000-C000-000000000046}",
+ axcontrol.IID_IOleObject,
+ 0,
+ (0,),
+ ocscom,
+ stcom,
+ )
+
+ mf=win32ui.GetMainFrame()
+ hwnd=mf.GetSafeHwnd()
+
+ # Set the host and document name
+ # for unknown reason document name becomes hostname, and document name
+ # is not set, debugged it, but don't know where the problem is?
+ oocom.SetHostNames("OTPython", "This is Cool")
+
+ # activate the OLE document
+ oocom.DoVerb( -1, ocscom, 0, hwnd, mf.GetWindowRect())
+
+ # set the hostnames again
+ oocom.SetHostNames("OTPython2", "ThisisCool2")
+
+ # get IDispatch of Word
+ doc=win32com.client.Dispatch(oocom.QueryInterface(pythoncom.IID_IDispatch))
+
+ # get IPersistStorage of Word
+ dpcom=oocom.QueryInterface(pythoncom.IID_IPersistStorage)
+
+ # let our ClientSite know the interfaces
+ ocs.SetIPersistStorage(dpcom)
+ ocs.SetIStorage(stcom)
+
+ # use IDispatch to do the Office Word test
+ # pasted from TestOffice.py
+
+ wrange = doc.Range()
+ for i in range(10):
+ wrange.InsertAfter("Hello from Python %d\n" % i)
+ paras = doc.Paragraphs
+ for i in range(len(paras)):
+ paras[i]().Font.ColorIndex = i+1
+ paras[i]().Font.Size = 12 + (4 * i)
+ # XXX - note that
+ # for para in paras:
+ # para().Font...
+ # doesnt seem to work - no error, just doesnt work
+ # Should check if it works for VB!
+
+
+ dpcom.Save(stcom, 0)
+ dpcom.HandsOffStorage()
+# oocom.Close(axcontrol.OLECLOSE_NOSAVE) # or OLECLOSE_SAVEIFDIRTY, but it fails???
+
+ #Save the ILockBytes data to "persist2.doc"
+ lbcom.Flush()
+
+ #exiting Winword will automatically update the ILockBytes data
+ #and flush it to "%TEMP%\persist.doc"
+ doc.Application.Quit()
+
+if __name__=='__main__':
+ test()
+ pythoncom.CoUninitialize()
+ CheckClean()
diff --git a/Lib/site-packages/win32com/test/testPippo.py b/Lib/site-packages/win32com/test/testPippo.py
new file mode 100644
index 0000000..29c821d
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testPippo.py
@@ -0,0 +1,55 @@
+import sys
+import unittest
+import pythoncom
+from win32com.client import Dispatch
+from win32com.client.gencache import EnsureDispatch
+
+class PippoTester(unittest.TestCase):
+ def setUp(self):
+ from win32com.test.util import RegisterPythonServer
+ from win32com.test import pippo_server
+ RegisterPythonServer(pippo_server.__file__, "Python.Test.Pippo")
+ # create it.
+ self.object = Dispatch("Python.Test.Pippo")
+
+ def testLeaks(self):
+ try:
+ gtrc = sys.gettotalrefcount
+ except AttributeError:
+ print("Please run this with python_d for leak tests")
+ gtrc = lambda: 0
+ # note creating self.object() should have consumed our "one time" leaks
+ self.object.Method1()
+ start = gtrc()
+ for i in range(1000):
+ object = Dispatch("Python.Test.Pippo")
+ object.Method1()
+ object = None
+ end = gtrc()
+ if end-start > 5:
+ self.fail("We lost %d references!" % (end-start,))
+
+ def testResults(self):
+ rc, out1 = self.object.Method2(123, 111)
+ self.failUnlessEqual(rc, 123)
+ self.failUnlessEqual(out1, 222)
+
+ def testLeaksGencache(self):
+ try:
+ gtrc = sys.gettotalrefcount
+ except AttributeError:
+ print("Please run this with python_d for leak tests")
+ gtrc = lambda: 0
+ # note creating self.object() should have consumed our "one time" leaks
+ object = EnsureDispatch("Python.Test.Pippo")
+ start = gtrc()
+ for i in range(1000):
+ object = EnsureDispatch("Python.Test.Pippo")
+ object.Method1()
+ object = None
+ end = gtrc()
+ if end-start > 10:
+ self.fail("We lost %d references!" % (end-start,))
+
+if __name__=='__main__':
+ unittest.main()
diff --git a/Lib/site-packages/win32com/test/testPyComTest.py b/Lib/site-packages/win32com/test/testPyComTest.py
new file mode 100644
index 0000000..0608207
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testPyComTest.py
@@ -0,0 +1,691 @@
+# NOTE - Still seems to be a leak here somewhere
+# gateway count doesnt hit zero. Hence the print statements!
+
+import sys; sys.coinit_flags=0 # Must be free-threaded!
+import win32api, pythoncom, time
+import pywintypes
+import os
+import winerror
+import win32com
+import win32com.client.connect
+from win32com.test.util import CheckClean
+from win32com.client import constants, DispatchBaseClass, CastTo, VARIANT
+from win32com.test.util import RegisterPythonServer
+from pywin32_testutil import str2memory
+import datetime
+import decimal
+import win32timezone
+
+importMsg = "**** PyCOMTest is not installed ***\n PyCOMTest is a Python test specific COM client and server.\n It is likely this server is not installed on this machine\n To install the server, you must get the win32com sources\n and build it using MS Visual C++"
+
+error = Exception
+
+# This test uses a Python implemented COM server - ensure correctly registered.
+RegisterPythonServer(os.path.join(os.path.dirname(__file__), '..', "servers", "test_pycomtest.py"),
+ "Python.Test.PyCOMTest")
+
+from win32com.client import gencache
+try:
+ gencache.EnsureModule('{6BCDCB60-5605-11D0-AE5F-CADD4C000000}', 0, 1, 1)
+except pythoncom.com_error:
+ print("The PyCOMTest module can not be located or generated.")
+ print(importMsg)
+ raise RuntimeError(importMsg)
+
+# We had a bg where RegisterInterfaces would fail if gencache had
+# already been run - exercise that here
+from win32com import universal
+universal.RegisterInterfaces('{6BCDCB60-5605-11D0-AE5F-CADD4C000000}', 0, 1, 1)
+
+verbose = 0
+
+# convert a normal int to a long int - used to avoid, eg, '1L' for py3k
+# friendliness
+def ensure_long(int_val):
+ if sys.version_info > (3,):
+ # py3k - no such thing as a 'long'
+ return int_val
+ # on py2x, we just use an expression that results in a long
+ return 0x100000000-0x100000000+int_val
+
+def check_get_set(func, arg):
+ got = func(arg)
+ if got != arg:
+ raise error("%s failed - expected %r, got %r" % (func, arg, got))
+
+def check_get_set_raises(exc, func, arg):
+ try:
+ got = func(arg)
+ except exc as e:
+ pass # what we expect!
+ else:
+ raise error("%s with arg %r didn't raise %s - returned %r" % (func, arg, exc, got))
+
+def progress(*args):
+ if verbose:
+ for arg in args:
+ print(arg, end=' ')
+ print()
+
+def TestApplyResult(fn, args, result):
+ try:
+ fnName = str(fn).split()[1]
+ except:
+ fnName = str(fn)
+ progress("Testing ", fnName)
+ pref = "function " + fnName
+ rc = fn(*args)
+ if rc != result:
+ raise error("%s failed - result not %r but %r" % (pref, result, rc))
+
+def TestConstant(constName, pyConst):
+ try:
+ comConst = getattr(constants, constName)
+ except:
+ raise error("Constant %s missing" % (constName,))
+ if comConst != pyConst:
+ raise error("Constant value wrong for %s - got %s, wanted %s" % (constName, comConst, pyConst))
+
+# Simple handler class. This demo only fires one event.
+class RandomEventHandler:
+ def _Init(self):
+ self.fireds = {}
+ def OnFire(self, no):
+ try:
+ self.fireds[no] = self.fireds[no] + 1
+ except KeyError:
+ self.fireds[no] = 0
+ def OnFireWithNamedParams(self, no, a_bool, out1, out2):
+ # This test exists mainly to help with an old bug, where named
+ # params would come in reverse.
+ Missing = pythoncom.Missing
+ if no is not Missing:
+ # We know our impl called 'OnFire' with the same ID
+ assert no in self.fireds
+ assert no+1==out1, "expecting 'out1' param to be ID+1"
+ assert no+2==out2, "expecting 'out2' param to be ID+2"
+ # The middle must be a boolean.
+ assert a_bool is Missing or type(a_bool)==bool, "middle param not a bool"
+ return out1+2, out2+2
+
+ def _DumpFireds(self):
+ if not self.fireds:
+ print("ERROR: Nothing was received!")
+ for firedId, no in self.fireds.items():
+ progress("ID %d fired %d times" % (firedId, no))
+
+# Test everything which can be tested using both the "dynamic" and "generated"
+# COM objects (or when there are very subtle differences)
+def TestCommon(o, is_generated):
+ progress("Getting counter")
+ counter = o.GetSimpleCounter()
+ TestCounter(counter, is_generated)
+
+ progress("Checking default args")
+ rc = o.TestOptionals()
+ if rc[:-1] != ("def", 0, 1) or abs(rc[-1]-3.14)>.01:
+ print(rc)
+ raise error("Did not get the optional values correctly")
+ rc = o.TestOptionals("Hi", 2, 3, 1.1)
+ if rc[:-1] != ("Hi", 2, 3) or abs(rc[-1]-1.1)>.01:
+ print(rc)
+ raise error("Did not get the specified optional values correctly")
+ rc = o.TestOptionals2(0)
+ if rc != (0, "", 1):
+ print(rc)
+ raise error("Did not get the optional2 values correctly")
+ rc = o.TestOptionals2(1.1, "Hi", 2)
+ if rc[1:] != ("Hi", 2) or abs(rc[0]-1.1)>.01:
+ print(rc)
+ raise error("Did not get the specified optional2 values correctly")
+
+ progress("Checking getting/passing IUnknown")
+ check_get_set(o.GetSetUnknown, o)
+ progress("Checking getting/passing IDispatch")
+ if not isinstance(o.GetSetDispatch(o), o.__class__):
+ raise error("GetSetDispatch failed: %r" % (o.GetSetDispatch(o),))
+ progress("Checking getting/passing IDispatch of known type")
+ if o.GetSetInterface(o).__class__ != o.__class__:
+ raise error("GetSetDispatch failed")
+
+ progress("Checking misc args")
+ check_get_set(o.GetSetVariant, 4)
+ check_get_set(o.GetSetVariant, "foo")
+ check_get_set(o.GetSetVariant, o)
+
+ # signed/unsigned.
+ check_get_set(o.GetSetInt, 0)
+ check_get_set(o.GetSetInt, -1)
+ check_get_set(o.GetSetInt, 1)
+
+ check_get_set(o.GetSetUnsignedInt, 0)
+ check_get_set(o.GetSetUnsignedInt, 1)
+ check_get_set(o.GetSetUnsignedInt, 0x80000000)
+ if o.GetSetUnsignedInt(-1) != 0xFFFFFFFF:
+ # -1 is a special case - we accept a negative int (silently converting to
+ # unsigned) but when getting it back we convert it to a long.
+ raise error("unsigned -1 failed")
+
+ check_get_set(o.GetSetLong, 0)
+ check_get_set(o.GetSetLong, -1)
+ check_get_set(o.GetSetLong, 1)
+
+ check_get_set(o.GetSetUnsignedLong, 0)
+ check_get_set(o.GetSetUnsignedLong, 1)
+ check_get_set(o.GetSetUnsignedLong, 0x80000000)
+ # -1 is a special case - see above.
+ if o.GetSetUnsignedLong(-1) != 0xFFFFFFFF:
+ raise error("unsigned -1 failed")
+
+ # We want to explicitly test > 32 bits. py3k has no 'maxint' and
+ # 'maxsize+1' is no good on 64bit platforms as its 65 bits!
+ big = 2147483647 # sys.maxint on py2k
+ for l in big, big+1, 1 << 65:
+ check_get_set(o.GetSetVariant, l)
+
+ progress("Checking structs")
+ r = o.GetStruct()
+ assert r.int_value == 99 and str(r.str_value)=="Hello from C++"
+ assert o.DoubleString("foo") == "foofoo"
+
+ progress("Checking var args")
+ o.SetVarArgs("Hi", "There", "From", "Python", 1)
+ if o.GetLastVarArgs() != ("Hi", "There", "From", "Python", 1):
+ raise error("VarArgs failed -" + str(o.GetLastVarArgs()))
+
+ progress("Checking arrays")
+ l=[]
+ TestApplyResult(o.SetVariantSafeArray, (l,), len(l))
+ l=[1,2,3,4]
+ TestApplyResult(o.SetVariantSafeArray, (l,), len(l))
+ TestApplyResult(o.CheckVariantSafeArray, ((1,2,3,4,),), 1)
+
+ # and binary
+ TestApplyResult(o.SetBinSafeArray, (str2memory('foo\0bar'),), 7)
+
+ progress("Checking properties")
+ o.LongProp = 3
+ if o.LongProp != 3 or o.IntProp != 3:
+ raise error("Property value wrong - got %d/%d" % (o.LongProp,o.IntProp))
+ o.LongProp = o.IntProp = -3
+ if o.LongProp != -3 or o.IntProp != -3:
+ raise error("Property value wrong - got %d/%d" % (o.LongProp,o.IntProp))
+ # This number fits in an unsigned long. Attempting to set it to a normal
+ # long will involve overflow, which is to be expected. But we do
+ # expect it to work in a property explicitly a VT_UI4.
+ check = 3 *10 **9
+ o.ULongProp = check
+ if o.ULongProp != check:
+ raise error("Property value wrong - got %d (expected %d)" % (o.ULongProp, check))
+
+ TestApplyResult(o.Test, ("Unused", 99), 1) # A bool function
+ TestApplyResult(o.Test, ("Unused", -1), 1) # A bool function
+ TestApplyResult(o.Test, ("Unused", 1==1), 1) # A bool function
+ TestApplyResult(o.Test, ("Unused", 0), 0)
+ TestApplyResult(o.Test, ("Unused", 1==0), 0)
+
+ assert o.DoubleString("foo") == "foofoo"
+
+ TestConstant("ULongTest1", ensure_long(0xFFFFFFFF))
+ TestConstant("ULongTest2", ensure_long(0x7FFFFFFF))
+ TestConstant("LongTest1", ensure_long(-0x7FFFFFFF))
+ TestConstant("LongTest2", ensure_long(0x7FFFFFFF))
+ TestConstant("UCharTest", 255)
+ TestConstant("CharTest", -1)
+ # 'Hello Loraine', but the 'r' is the "Registered" sign (\xae)
+ TestConstant("StringTest", "Hello Lo\xaeaine")
+
+ progress("Checking dates and times")
+ if issubclass(pywintypes.TimeType, datetime.datetime):
+ # For now *all* times passed must be tz-aware.
+ now = win32timezone.now()
+ # but conversion to and from a VARIANT loses sub-second...
+ now = now.replace(microsecond=0)
+ later = now + datetime.timedelta(seconds=1)
+ TestApplyResult(o.EarliestDate, (now, later), now)
+ else:
+ # old PyTime object
+ now = pythoncom.MakeTime(time.gmtime(time.time()))
+ later = pythoncom.MakeTime(time.gmtime(time.time()+1))
+ TestApplyResult(o.EarliestDate, (now, later), now)
+ # But it can still *accept* tz-naive datetime objects...
+ now = datetime.datetime.now()
+ expect = pythoncom.MakeTime(now)
+ TestApplyResult(o.EarliestDate, (now, now), expect)
+
+ progress("Checking currency")
+ # currency.
+ pythoncom.__future_currency__ = 1
+ if o.CurrencyProp != 0:
+ raise error("Expecting 0, got %r" % (o.CurrencyProp,))
+ for val in ("1234.5678", "1234.56", "1234"):
+ o.CurrencyProp = decimal.Decimal(val)
+ if o.CurrencyProp != decimal.Decimal(val):
+ raise error("%s got %r" % (val, o.CurrencyProp))
+ v1 = decimal.Decimal("1234.5678")
+ TestApplyResult(o.DoubleCurrency, (v1,), v1*2)
+
+ v2 = decimal.Decimal("9012.3456")
+ TestApplyResult(o.AddCurrencies, (v1, v2), v1+v2)
+
+ TestTrickyTypesWithVariants(o, is_generated)
+ progress("Checking win32com.client.VARIANT")
+ TestPyVariant(o, is_generated)
+
+
+def TestTrickyTypesWithVariants(o, is_generated):
+ # Test tricky stuff with type handling and generally only works with
+ # "generated" support but can be worked around using VARIANT.
+ if is_generated:
+ got = o.TestByRefVariant(2)
+ else:
+ v = VARIANT(pythoncom.VT_BYREF | pythoncom.VT_VARIANT, 2)
+ o.TestByRefVariant(v)
+ got = v.value
+ if got != 4:
+ raise error("TestByRefVariant failed")
+
+ if is_generated:
+ got = o.TestByRefString("Foo")
+ else:
+ v = VARIANT(pythoncom.VT_BYREF | pythoncom.VT_BSTR, "Foo")
+ o.TestByRefString(v)
+ got = v.value
+ if got != "FooFoo":
+ raise error("TestByRefString failed")
+
+ # check we can pass ints as a VT_UI1
+ vals=[1,2,3,4]
+ if is_generated:
+ arg = vals
+ else:
+ arg = VARIANT(pythoncom.VT_ARRAY | pythoncom.VT_UI1, vals)
+ TestApplyResult(o.SetBinSafeArray, (arg,), len(vals))
+
+ # safearrays of doubles and floats
+ vals = [0, 1.1, 2.2, 3.3]
+ if is_generated:
+ arg = vals
+ else:
+ arg = VARIANT(pythoncom.VT_ARRAY | pythoncom.VT_R8, vals)
+ TestApplyResult(o.SetDoubleSafeArray, (arg,), len(vals))
+
+ if is_generated:
+ arg = vals
+ else:
+ arg = VARIANT(pythoncom.VT_ARRAY | pythoncom.VT_R4, vals)
+ TestApplyResult(o.SetFloatSafeArray, (arg,), len(vals))
+
+ vals=[1.1, 2.2, 3.3, 4.4]
+ expected = (1.1*2, 2.2*2, 3.3*2, 4.4*2)
+ if is_generated:
+ TestApplyResult(o.ChangeDoubleSafeArray, (vals,), expected)
+ else:
+ arg = VARIANT(pythoncom.VT_BYREF | pythoncom.VT_ARRAY | pythoncom.VT_R8, vals)
+ o.ChangeDoubleSafeArray(arg)
+ if arg.value != expected:
+ raise error("ChangeDoubleSafeArray got the wrong value")
+
+ if is_generated:
+ got = o.DoubleInOutString("foo")
+ else:
+ v = VARIANT(pythoncom.VT_BYREF | pythoncom.VT_BSTR, "foo")
+ o.DoubleInOutString(v)
+ got = v.value
+ assert got == "foofoo", got
+
+ val = decimal.Decimal("1234.5678")
+ if is_generated:
+ got = o.DoubleCurrencyByVal(val)
+ else:
+ v = VARIANT(pythoncom.VT_BYREF | pythoncom.VT_CY, val)
+ o.DoubleCurrencyByVal(v)
+ got = v.value
+ assert got == val * 2
+
+def TestDynamic():
+ progress("Testing Dynamic")
+ import win32com.client.dynamic
+ o = win32com.client.dynamic.DumbDispatch("PyCOMTest.PyCOMTest")
+ TestCommon(o, False)
+
+ counter = win32com.client.dynamic.DumbDispatch("PyCOMTest.SimpleCounter")
+ TestCounter(counter, False)
+
+ # Dynamic doesn't know this should be an int, so we get a COM
+ # TypeMismatch error.
+ try:
+ check_get_set_raises(ValueError, o.GetSetInt, "foo")
+ raise error("no exception raised")
+ except pythoncom.com_error as exc:
+ if exc.hresult != winerror.DISP_E_TYPEMISMATCH:
+ raise
+
+ # damn - props with params don't work for dynamic objects :(
+ # o.SetParamProp(0, 1)
+ # if o.ParamProp(0) != 1:
+ # raise RuntimeError, o.paramProp(0)
+
+
+def TestGenerated():
+ # Create an instance of the server.
+ from win32com.client.gencache import EnsureDispatch
+ o = EnsureDispatch("PyCOMTest.PyCOMTest")
+ TestCommon(o, True)
+
+ counter = EnsureDispatch("PyCOMTest.SimpleCounter")
+ TestCounter(counter, True)
+
+ # XXX - this is failing in dynamic tests, but should work fine.
+ i1, i2 = o.GetMultipleInterfaces()
+ if not isinstance(i1, DispatchBaseClass) or not isinstance(i2, DispatchBaseClass):
+ # Yay - is now an instance returned!
+ raise error("GetMultipleInterfaces did not return instances - got '%s', '%s'" % (i1, i2))
+ del i1
+ del i2
+
+ # Generated knows to only pass a 32bit int, so should fail.
+ check_get_set_raises(OverflowError, o.GetSetInt, 0x80000000)
+ check_get_set_raises(OverflowError, o.GetSetLong, 0x80000000)
+
+ # Generated knows this should be an int, so raises ValueError
+ check_get_set_raises(ValueError, o.GetSetInt, "foo")
+ check_get_set_raises(ValueError, o.GetSetLong, "foo")
+
+ # Pass some non-sequence objects to our array decoder, and watch it fail.
+ try:
+ o.SetVariantSafeArray("foo")
+ raise error("Expected a type error")
+ except TypeError:
+ pass
+ try:
+ o.SetVariantSafeArray(666)
+ raise error("Expected a type error")
+ except TypeError:
+ pass
+
+ o.GetSimpleSafeArray(None)
+ TestApplyResult(o.GetSimpleSafeArray, (None,), tuple(range(10)))
+ resultCheck = tuple(range(5)), tuple(range(10)), tuple(range(20))
+ TestApplyResult(o.GetSafeArrays, (None, None, None), resultCheck)
+
+ l=[]
+ TestApplyResult(o.SetIntSafeArray, (l,), len(l))
+ l=[1,2,3,4]
+ TestApplyResult(o.SetIntSafeArray, (l,), len(l))
+ ll=[1,2,3,0x100000000]
+ TestApplyResult(o.SetLongLongSafeArray, (ll,), len(ll))
+ TestApplyResult(o.SetULongLongSafeArray, (ll,), len(ll))
+
+ # Tell the server to do what it does!
+ TestApplyResult(o.Test2, (constants.Attr2,), constants.Attr2)
+ TestApplyResult(o.Test3, (constants.Attr2,), constants.Attr2)
+ TestApplyResult(o.Test4, (constants.Attr2,), constants.Attr2)
+ TestApplyResult(o.Test5, (constants.Attr2,), constants.Attr2)
+
+ TestApplyResult(o.Test6, (constants.WideAttr1,), constants.WideAttr1)
+ TestApplyResult(o.Test6, (constants.WideAttr2,), constants.WideAttr2)
+ TestApplyResult(o.Test6, (constants.WideAttr3,), constants.WideAttr3)
+ TestApplyResult(o.Test6, (constants.WideAttr4,), constants.WideAttr4)
+ TestApplyResult(o.Test6, (constants.WideAttr5,), constants.WideAttr5)
+
+ o.SetParamProp(0, 1)
+ if o.ParamProp(0) != 1:
+ raise RuntimeError(o.paramProp(0))
+
+ # Make sure CastTo works - even though it is only casting it to itself!
+ o2 = CastTo(o, "IPyCOMTest")
+ if o != o2:
+ raise error("CastTo should have returned the same object")
+
+ # Do the connection point thing...
+ # Create a connection object.
+ progress("Testing connection points")
+ o2 = win32com.client.DispatchWithEvents(o, RandomEventHandler)
+ TestEvents(o2, o2)
+ # and a plain "WithEvents".
+ handler = win32com.client.WithEvents(o, RandomEventHandler)
+ TestEvents(o, handler)
+ progress("Finished generated .py test.")
+
+
+def TestEvents(o, handler):
+ sessions = []
+ handler._Init()
+ try:
+ for i in range(3):
+ session = o.Start()
+ sessions.append(session)
+ time.sleep(.5)
+ finally:
+ # Stop the servers
+ for session in sessions:
+ o.Stop(session)
+ handler._DumpFireds()
+ handler.close()
+
+
+def _TestPyVariant(o, is_generated, val, checker = None):
+ if is_generated:
+ vt, got = o.GetVariantAndType(val)
+ else:
+ # Gotta supply all 3 args with the last 2 being explicit variants to
+ # get the byref behaviour.
+ var_vt = VARIANT(pythoncom.VT_UI2 | pythoncom.VT_BYREF, 0)
+ var_result = VARIANT(pythoncom.VT_VARIANT | pythoncom.VT_BYREF, 0)
+ o.GetVariantAndType(val, var_vt, var_result)
+ vt = var_vt.value
+ got = var_result.value
+ if checker is not None:
+ checker(got)
+ return
+ # default checking.
+ assert vt == val.varianttype, (vt, val.varianttype)
+ # Handle our safe-array test - if the passed value is a list of variants,
+ # compare against the actual values.
+ if type(val.value) in (tuple, list):
+ check = [v.value if isinstance(v, VARIANT) else v for v in val.value]
+ # pythoncom always returns arrays as tuples.
+ got = list(got)
+ else:
+ check = val.value
+ assert type(check) == type(got), (type(check), type(got))
+ assert check == got, (check, got)
+
+def _TestPyVariantFails(o, is_generated, val, exc):
+ try:
+ _TestPyVariant(o, is_generated, val)
+ raise error("Setting %r didn't raise %s" % (val, exc))
+ except exc:
+ pass
+
+def TestPyVariant(o, is_generated):
+ _TestPyVariant(o, is_generated, VARIANT(pythoncom.VT_UI1, 1))
+ _TestPyVariant(o, is_generated, VARIANT(pythoncom.VT_ARRAY | pythoncom.VT_UI4, [1,2,3]))
+ _TestPyVariant(o, is_generated, VARIANT(pythoncom.VT_BSTR, "hello"))
+ _TestPyVariant(o, is_generated, VARIANT(pythoncom.VT_ARRAY | pythoncom.VT_BSTR, ["hello", "there"]))
+ def check_dispatch(got):
+ assert isinstance(got._oleobj_, pythoncom.TypeIIDs[pythoncom.IID_IDispatch])
+ _TestPyVariant(o, is_generated, VARIANT(pythoncom.VT_DISPATCH, o), check_dispatch)
+ _TestPyVariant(o, is_generated, VARIANT(pythoncom.VT_ARRAY | pythoncom.VT_DISPATCH, [o]))
+ # an array of variants each with a specific type.
+ v = VARIANT(pythoncom.VT_ARRAY | pythoncom.VT_VARIANT,
+ [VARIANT(pythoncom.VT_UI4, 1),
+ VARIANT(pythoncom.VT_UI4, 2),
+ VARIANT(pythoncom.VT_UI4, 3)
+ ]
+ )
+ _TestPyVariant(o, is_generated, v)
+
+ # and failures
+ _TestPyVariantFails(o, is_generated, VARIANT(pythoncom.VT_UI1, "foo"), ValueError)
+
+
+def TestCounter(counter, bIsGenerated):
+ # Test random access into container
+ progress("Testing counter", repr(counter))
+ import random
+ for i in range(50):
+ num = int(random.random() * len(counter))
+ try:
+ ret = counter[num]
+ if ret != num+1:
+ raise error("Random access into element %d failed - return was %s" % (num,repr(ret)))
+ except IndexError:
+ raise error("** IndexError accessing collection element %d" % num)
+
+ num = 0
+ if bIsGenerated:
+ counter.SetTestProperty(1)
+ counter.TestProperty = 1 # Note this has a second, default arg.
+ counter.SetTestProperty(1,2)
+ if counter.TestPropertyWithDef != 0:
+ raise error("Unexpected property set value!")
+ if counter.TestPropertyNoDef(1) != 1:
+ raise error("Unexpected property set value!")
+ else:
+ pass
+ # counter.TestProperty = 1
+
+ counter.LBound=1
+ counter.UBound=10
+ if counter.LBound != 1 or counter.UBound!=10:
+ print("** Error - counter did not keep its properties")
+
+ if bIsGenerated:
+ bounds = counter.GetBounds()
+ if bounds[0]!=1 or bounds[1]!=10:
+ raise error("** Error - counter did not give the same properties back")
+ counter.SetBounds(bounds[0], bounds[1])
+
+ for item in counter:
+ num = num + 1
+ if num != len(counter):
+ raise error("*** Length of counter and loop iterations dont match ***")
+ if num != 10:
+ raise error("*** Unexpected number of loop iterations ***")
+
+ counter = counter._enum_.Clone() # Test Clone() and enum directly
+ counter.Reset()
+ num = 0
+ for item in counter:
+ num = num + 1
+ if num != 10:
+ raise error("*** Unexpected number of loop iterations - got %d ***" % num)
+ progress("Finished testing counter")
+
+def TestLocalVTable(ob):
+ # Python doesn't fully implement this interface.
+ if ob.DoubleString("foo") != "foofoo":
+ raise error("couldn't foofoo")
+
+###############################
+##
+## Some vtable tests of the interface
+##
+def TestVTable(clsctx=pythoncom.CLSCTX_ALL):
+ # Any vtable interfaces marked as dual *should* be able to be
+ # correctly implemented as IDispatch.
+ ob = win32com.client.Dispatch("Python.Test.PyCOMTest")
+ TestLocalVTable(ob)
+ # Now test it via vtable - use some C++ code to help here as Python can't do it directly yet.
+ tester = win32com.client.Dispatch("PyCOMTest.PyCOMTest")
+ testee = pythoncom.CoCreateInstance("Python.Test.PyCOMTest", None, clsctx, pythoncom.IID_IUnknown)
+ # check we fail gracefully with None passed.
+ try:
+ tester.TestMyInterface(None)
+ except pythoncom.com_error as details:
+ pass
+ # and a real object.
+ tester.TestMyInterface(testee)
+
+def TestVTable2():
+ # We once crashed creating our object with the native interface as
+ # the first IID specified. We must do it _after_ the tests, so that
+ # Python has already had the gateway registered from last run.
+ ob = win32com.client.Dispatch("Python.Test.PyCOMTest")
+ iid = pythoncom.InterfaceNames["IPyCOMTest"]
+ clsid = "Python.Test.PyCOMTest"
+ clsctx = pythoncom.CLSCTX_SERVER
+ try:
+ testee = pythoncom.CoCreateInstance(clsid, None, clsctx, iid)
+ except TypeError:
+ # Python can't actually _use_ this interface yet, so this is
+ # "expected". Any COM error is not.
+ pass
+
+def TestVTableMI():
+ clsctx = pythoncom.CLSCTX_SERVER
+ ob = pythoncom.CoCreateInstance("Python.Test.PyCOMTestMI", None, clsctx, pythoncom.IID_IUnknown)
+ # This inherits from IStream.
+ ob.QueryInterface(pythoncom.IID_IStream)
+ # This implements IStorage, specifying the IID as a string
+ ob.QueryInterface(pythoncom.IID_IStorage)
+ # IDispatch should always work
+ ob.QueryInterface(pythoncom.IID_IDispatch)
+
+ iid = pythoncom.InterfaceNames["IPyCOMTest"]
+ try:
+ ob.QueryInterface(iid)
+ except TypeError:
+ # Python can't actually _use_ this interface yet, so this is
+ # "expected". Any COM error is not.
+ pass
+
+def TestQueryInterface(long_lived_server = 0, iterations=5):
+ tester = win32com.client.Dispatch("PyCOMTest.PyCOMTest")
+ if long_lived_server:
+ # Create a local server
+ t0 = win32com.client.Dispatch("Python.Test.PyCOMTest", clsctx=pythoncom.CLSCTX_LOCAL_SERVER)
+ # Request custom interfaces a number of times
+ prompt = [
+ "Testing QueryInterface without long-lived local-server #%d of %d...",
+ "Testing QueryInterface with long-lived local-server #%d of %d..."
+ ]
+
+ for i in range(iterations):
+ progress(prompt[long_lived_server!=0] % (i+1, iterations))
+ tester.TestQueryInterface()
+
+class Tester(win32com.test.util.TestCase):
+ def testVTableInProc(self):
+ # We used to crash running this the second time - do it a few times
+ for i in range(3):
+ progress("Testing VTables in-process #%d..." % (i+1))
+ TestVTable(pythoncom.CLSCTX_INPROC_SERVER)
+ def testVTableLocalServer(self):
+ for i in range(3):
+ progress("Testing VTables out-of-process #%d..." % (i+1))
+ TestVTable(pythoncom.CLSCTX_LOCAL_SERVER)
+ def testVTable2(self):
+ for i in range(3):
+ TestVTable2()
+ def testVTableMI(self):
+ for i in range(3):
+ TestVTableMI()
+ def testMultiQueryInterface(self):
+ TestQueryInterface(0,6)
+ # When we use the custom interface in the presence of a long-lived
+ # local server, i.e. a local server that is already running when
+ # we request an instance of our COM object, and remains afterwards,
+ # then after repeated requests to create an instance of our object
+ # the custom interface disappears -- i.e. QueryInterface fails with
+ # E_NOINTERFACE. Set the upper range of the following test to 2 to
+ # pass this test, i.e. TestQueryInterface(1,2)
+ TestQueryInterface(1,6)
+ def testDynamic(self):
+ TestDynamic()
+ def testGenerated(self):
+ TestGenerated()
+
+if __name__=='__main__':
+ # XXX - todo - Complete hack to crank threading support.
+ # Should NOT be necessary
+ def NullThreadFunc():
+ pass
+ import _thread
+ _thread.start_new( NullThreadFunc, () )
+
+ if "-v" in sys.argv: verbose = 1
+
+ win32com.test.util.testmain()
diff --git a/Lib/site-packages/win32com/test/testPyScriptlet.js b/Lib/site-packages/win32com/test/testPyScriptlet.js
new file mode 100644
index 0000000..c232065
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testPyScriptlet.js
@@ -0,0 +1,33 @@
+function print(msg)
+{
+ WScript.Echo(msg) ;
+}
+
+function check(condition, msg)
+{
+ if (!condition) {
+ print("***** testPyScriptlet.js failed *****");
+ print(msg);
+ }
+}
+
+var thisScriptEngine = ScriptEngine() ;
+
+var majorVersion = ScriptEngineMajorVersion() ;
+var minorVersion = ScriptEngineMinorVersion() ;
+var buildVersion = ScriptEngineBuildVersion() ;
+
+WScript.Echo(thisScriptEngine + " Version " + majorVersion + "." + minorVersion + " Build " + buildVersion) ;
+
+var scriptlet = new ActiveXObject("TestPys.Scriptlet") ;
+
+check(scriptlet.PyProp1=="PyScript Property1", "PyProp1 wasn't correct initial value");
+scriptlet.PyProp1 = "New Value";
+check(scriptlet.PyProp1=="New Value", "PyProp1 wasn't correct new value");
+
+check(scriptlet.PyProp2=="PyScript Property2", "PyProp2 wasn't correct initial value");
+scriptlet.PyProp2 = "Another New Value";
+check(scriptlet.PyProp2=="Another New Value", "PyProp2 wasn't correct new value");
+
+check(scriptlet.PyMethod1()=="PyMethod1 called", "Method1 wrong value");
+check(scriptlet.PyMethod2()=="PyMethod2 called", "Method2 wrong value");
diff --git a/Lib/site-packages/win32com/test/testROT.py b/Lib/site-packages/win32com/test/testROT.py
new file mode 100644
index 0000000..8490b12
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testROT.py
@@ -0,0 +1,26 @@
+import pythoncom
+import unittest
+import win32com.test.util
+import winerror
+
+class TestROT(win32com.test.util.TestCase):
+ def testit(self):
+ ctx = pythoncom.CreateBindCtx()
+ rot = pythoncom.GetRunningObjectTable()
+ num = 0
+ for mk in rot:
+ name = mk.GetDisplayName(ctx, None)
+ num += 1
+ # Monikers themselves can iterate their contents (sometimes :)
+ try:
+ for sub in mk:
+ num += 1
+ except pythoncom.com_error as exc:
+ if exc.hresult != winerror.E_NOTIMPL:
+ raise
+
+ #if num < 2:
+ # print "Only", num, "objects in the ROT - this is unusual"
+
+if __name__=='__main__':
+ unittest.main()
diff --git a/Lib/site-packages/win32com/test/testServers.py b/Lib/site-packages/win32com/test/testServers.py
new file mode 100644
index 0000000..da00186
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testServers.py
@@ -0,0 +1,39 @@
+import pythoncom, win32com.client.dynamic, sys
+import winerror
+import win32com.test.util
+import unittest
+
+def TestConnections():
+ import win32com.demos.connect
+ win32com.demos.connect.test()
+
+class InterpCase(win32com.test.util.TestCase):
+ def setUp(self):
+ # Ensure the correct version registered.
+ from win32com.test.util import RegisterPythonServer
+ from win32com.servers import interp
+ RegisterPythonServer(interp.__file__, "Python.Interpreter")
+
+ def _testInterp(self, interp):
+ self.assertEquals(interp.Eval("1+1"), 2)
+ win32com.test.util.assertRaisesCOM_HRESULT(self, winerror.DISP_E_TYPEMISMATCH,
+ interp.Eval, 2)
+
+ def testInproc(self):
+ interp = win32com.client.dynamic.Dispatch("Python.Interpreter", clsctx = pythoncom.CLSCTX_INPROC)
+ self._testInterp(interp)
+
+ def testLocalServer(self):
+ interp = win32com.client.dynamic.Dispatch("Python.Interpreter", clsctx = pythoncom.CLSCTX_LOCAL_SERVER)
+ self._testInterp(interp)
+
+ def testAny(self):
+ interp = win32com.client.dynamic.Dispatch("Python.Interpreter")
+ self._testInterp(interp)
+
+class ConnectionsTestCase(win32com.test.util.TestCase):
+ def testConnections(self):
+ TestConnections()
+
+if __name__=='__main__':
+ unittest.main('testServers')
diff --git a/Lib/site-packages/win32com/test/testShell.py b/Lib/site-packages/win32com/test/testShell.py
new file mode 100644
index 0000000..b859ae5
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testShell.py
@@ -0,0 +1,260 @@
+import sys, os
+import struct
+import unittest
+import copy
+import datetime
+import win32timezone
+
+try:
+ sys_maxsize = sys.maxsize # 2.6 and later - maxsize != maxint on 64bits
+except AttributeError:
+ sys_maxsize = sys.maxint
+
+import win32con
+import pythoncom
+import pywintypes
+from win32com.shell import shell
+from win32com.shell.shellcon import *
+from win32com.storagecon import *
+
+import win32com.test.util
+from pywin32_testutil import str2bytes
+
+class ShellTester(win32com.test.util.TestCase):
+ def testShellLink(self):
+ desktop = str(shell.SHGetSpecialFolderPath(0, CSIDL_DESKTOP))
+ num = 0
+ shellLink = pythoncom.CoCreateInstance(shell.CLSID_ShellLink, None, pythoncom.CLSCTX_INPROC_SERVER, shell.IID_IShellLink)
+ persistFile = shellLink.QueryInterface(pythoncom.IID_IPersistFile)
+ names = [os.path.join(desktop, n) for n in os.listdir(desktop)]
+ programs = str(shell.SHGetSpecialFolderPath(0, CSIDL_PROGRAMS))
+ names.extend([os.path.join(programs, n) for n in os.listdir(programs)])
+ for name in names:
+ try:
+ persistFile.Load(name,STGM_READ)
+ except pythoncom.com_error:
+ continue
+ # Resolve is slow - avoid it for our tests.
+ #shellLink.Resolve(0, shell.SLR_ANY_MATCH | shell.SLR_NO_UI)
+ fname, findData = shellLink.GetPath(0)
+ unc = shellLink.GetPath(shell.SLGP_UNCPRIORITY)[0]
+ num += 1
+ if num == 0:
+ # This isn't a fatal error, but is unlikely.
+ print("Could not find any links on your desktop or programs dir, which is unusual")
+
+ def testShellFolder(self):
+ sf = shell.SHGetDesktopFolder()
+ names_1 = []
+ for i in sf: # Magically calls EnumObjects
+ name = sf.GetDisplayNameOf(i, SHGDN_NORMAL)
+ names_1.append(name)
+
+ # And get the enumerator manually
+ enum = sf.EnumObjects(0, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN)
+ names_2 = []
+ for i in enum:
+ name = sf.GetDisplayNameOf(i, SHGDN_NORMAL)
+ names_2.append(name)
+ names_1.sort()
+ names_2.sort()
+ self.assertEqual(names_1, names_2)
+
+class PIDLTester(win32com.test.util.TestCase):
+ def _rtPIDL(self, pidl):
+ pidl_str = shell.PIDLAsString(pidl)
+ pidl_rt = shell.StringAsPIDL(pidl_str)
+ self.assertEqual(pidl_rt, pidl)
+ pidl_str_rt = shell.PIDLAsString(pidl_rt)
+ self.assertEqual(pidl_str_rt, pidl_str)
+
+ def _rtCIDA(self, parent, kids):
+ cida = parent, kids
+ cida_str = shell.CIDAAsString(cida)
+ cida_rt = shell.StringAsCIDA(cida_str)
+ self.assertEqual(cida, cida_rt)
+ cida_str_rt = shell.CIDAAsString(cida_rt)
+ self.assertEqual(cida_str_rt, cida_str)
+
+ def testPIDL(self):
+ # A PIDL of "\1" is: cb pidl cb
+ expect = str2bytes("\03\00" "\1" "\0\0")
+ self.assertEqual(shell.PIDLAsString([str2bytes("\1")]), expect)
+ self._rtPIDL([str2bytes("\0")])
+ self._rtPIDL([str2bytes("\1"), str2bytes("\2"), str2bytes("\3")])
+ self._rtPIDL([str2bytes("\0") * 2048] * 2048)
+ # PIDL must be a list
+ self.assertRaises(TypeError, shell.PIDLAsString, "foo")
+
+ def testCIDA(self):
+ self._rtCIDA([str2bytes("\0")], [ [str2bytes("\0")] ])
+ self._rtCIDA([str2bytes("\1")], [ [str2bytes("\2")] ])
+ self._rtCIDA([str2bytes("\0")], [ [str2bytes("\0")], [str2bytes("\1")], [str2bytes("\2")] ])
+
+ def testBadShortPIDL(self):
+ # A too-short child element: cb pidl cb
+ pidl = str2bytes("\01\00" "\1")
+ self.assertRaises(ValueError, shell.StringAsPIDL, pidl)
+
+ # ack - tried to test too long PIDLs, but a len of 0xFFFF may not
+ # always fail.
+
+class FILEGROUPDESCRIPTORTester(win32com.test.util.TestCase):
+ def _getTestTimes(self):
+ if issubclass(pywintypes.TimeType, datetime.datetime):
+ ctime = win32timezone.now()
+ atime = ctime + datetime.timedelta(seconds=1)
+ wtime = atime + datetime.timedelta(seconds=1)
+ else:
+ ctime = pywintypes.Time(11)
+ atime = pywintypes.Time(12)
+ wtime = pywintypes.Time(13)
+ return ctime, atime, wtime
+
+ def _testRT(self, fd):
+ fgd_string = shell.FILEGROUPDESCRIPTORAsString([fd])
+ fd2 = shell.StringAsFILEGROUPDESCRIPTOR(fgd_string)[0]
+
+ fd = fd.copy()
+ fd2 = fd2.copy()
+
+ # The returned objects *always* have dwFlags and cFileName.
+ if 'dwFlags' not in fd:
+ del fd2['dwFlags']
+ if 'cFileName' not in fd:
+ self.assertEqual(fd2['cFileName'], '')
+ del fd2['cFileName']
+
+ self.assertEqual(fd, fd2)
+
+ def _testSimple(self, make_unicode):
+ fgd = shell.FILEGROUPDESCRIPTORAsString([], make_unicode)
+ header = struct.pack("i", 0)
+ self.assertEqual(header, fgd[:len(header)])
+ self._testRT(dict())
+ d = dict()
+ fgd = shell.FILEGROUPDESCRIPTORAsString([d], make_unicode)
+ header = struct.pack("i", 1)
+ self.assertEqual(header, fgd[:len(header)])
+ self._testRT(d)
+
+ def testSimpleBytes(self):
+ self._testSimple(False)
+
+ def testSimpleUnicode(self):
+ self._testSimple(True)
+
+ def testComplex(self):
+ clsid = pythoncom.MakeIID("{CD637886-DB8B-4b04-98B5-25731E1495BE}")
+ ctime, atime, wtime = self._getTestTimes()
+ d = dict(cFileName="foo.txt",
+ clsid=clsid,
+ sizel=(1,2),
+ pointl=(3,4),
+ dwFileAttributes = win32con.FILE_ATTRIBUTE_NORMAL,
+ ftCreationTime=ctime,
+ ftLastAccessTime=atime,
+ ftLastWriteTime=wtime,
+ nFileSize=sys_maxsize + 1)
+ self._testRT(d)
+
+ def testUnicode(self):
+ # exercise a bug fixed in build 210 - multiple unicode objects failed.
+ ctime, atime, wtime = self._getTestTimes()
+ d = [dict(cFileName="foo.txt",
+ sizel=(1,2),
+ pointl=(3,4),
+ dwFileAttributes = win32con.FILE_ATTRIBUTE_NORMAL,
+ ftCreationTime=ctime,
+ ftLastAccessTime=atime,
+ ftLastWriteTime=wtime,
+ nFileSize=sys_maxsize + 1),
+ dict(cFileName="foo2.txt",
+ sizel=(1,2),
+ pointl=(3,4),
+ dwFileAttributes = win32con.FILE_ATTRIBUTE_NORMAL,
+ ftCreationTime=ctime,
+ ftLastAccessTime=atime,
+ ftLastWriteTime=wtime,
+ nFileSize=sys_maxsize + 1),
+ dict(cFileName="foo\xa9.txt",
+ sizel=(1,2),
+ pointl=(3,4),
+ dwFileAttributes = win32con.FILE_ATTRIBUTE_NORMAL,
+ ftCreationTime=ctime,
+ ftLastAccessTime=atime,
+ ftLastWriteTime=wtime,
+ nFileSize=sys_maxsize + 1),
+ ]
+ s = shell.FILEGROUPDESCRIPTORAsString(d, 1)
+ d2 = shell.StringAsFILEGROUPDESCRIPTOR(s)
+ # clobber 'dwFlags' - they are not expected to be identical
+ for t in d2:
+ del t['dwFlags']
+ self.assertEqual(d, d2)
+
+class FileOperationTester(win32com.test.util.TestCase):
+ def setUp(self):
+ import tempfile
+ self.src_name = os.path.join(tempfile.gettempdir(), "pywin32_testshell")
+ self.dest_name = os.path.join(tempfile.gettempdir(), "pywin32_testshell_dest")
+ self.test_data = str2bytes("Hello from\0Python")
+ f=open(self.src_name, "wb")
+ f.write(self.test_data)
+ f.close()
+ try:
+ os.unlink(self.dest_name)
+ except os.error:
+ pass
+
+ def tearDown(self):
+ for fname in (self.src_name, self.dest_name):
+ if os.path.isfile(fname):
+ os.unlink(fname)
+
+ def testCopy(self):
+ s = (0, # hwnd,
+ FO_COPY, #operation
+ self.src_name,
+ self.dest_name)
+
+ rc, aborted = shell.SHFileOperation(s)
+ self.failUnless(not aborted)
+ self.failUnlessEqual(0, rc)
+ self.failUnless(os.path.isfile(self.src_name))
+ self.failUnless(os.path.isfile(self.dest_name))
+
+ def testRename(self):
+ s = (0, # hwnd,
+ FO_RENAME, #operation
+ self.src_name,
+ self.dest_name)
+ rc, aborted = shell.SHFileOperation(s)
+ self.failUnless(not aborted)
+ self.failUnlessEqual(0, rc)
+ self.failUnless(os.path.isfile(self.dest_name))
+ self.failUnless(not os.path.isfile(self.src_name))
+
+ def testMove(self):
+ s = (0, # hwnd,
+ FO_MOVE, #operation
+ self.src_name,
+ self.dest_name)
+ rc, aborted = shell.SHFileOperation(s)
+ self.failUnless(not aborted)
+ self.failUnlessEqual(0, rc)
+ self.failUnless(os.path.isfile(self.dest_name))
+ self.failUnless(not os.path.isfile(self.src_name))
+
+ def testDelete(self):
+ s = (0, # hwnd,
+ FO_DELETE, #operation
+ self.src_name, None,
+ FOF_NOCONFIRMATION)
+ rc, aborted = shell.SHFileOperation(s)
+ self.failUnless(not aborted)
+ self.failUnlessEqual(0, rc)
+ self.failUnless(not os.path.isfile(self.src_name))
+
+if __name__=='__main__':
+ win32com.test.util.testmain()
diff --git a/Lib/site-packages/win32com/test/testStorage.py b/Lib/site-packages/win32com/test/testStorage.py
new file mode 100644
index 0000000..790d294
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testStorage.py
@@ -0,0 +1,62 @@
+from win32com import storagecon
+import pythoncom, os, win32api
+import win32com.test.util
+
+import unittest
+
+class TestEnum(win32com.test.util.TestCase):
+ def testit(self):
+ fname, tmp = win32api.GetTempFileName(win32api.GetTempPath(),'stg')
+ m=storagecon.STGM_READWRITE | storagecon.STGM_SHARE_EXCLUSIVE
+ ## file, mode, format, attrs (always 0), IID (IStorage or IPropertySetStorage, storage options(only used with STGFMT_DOCFILE)
+ pss=pythoncom.StgOpenStorageEx(fname, m, storagecon.STGFMT_FILE, 0 , pythoncom.IID_IPropertySetStorage)
+ ### {"Version":2,"reserved":0,"SectorSize":512,"TemplateFile":u'somefilename'})
+
+ ## FMTID_SummaryInformation FMTID_DocSummaryInformation FMTID_UserDefinedProperties
+ psuser=pss.Create(pythoncom.FMTID_UserDefinedProperties,
+ pythoncom.IID_IPropertySetStorage,
+ storagecon.PROPSETFLAG_DEFAULT,
+ storagecon.STGM_READWRITE|storagecon.STGM_CREATE|storagecon.STGM_SHARE_EXCLUSIVE) ## its very picky about flag combinations!
+ psuser.WriteMultiple((3,4),('hey','bubba'))
+ psuser.WritePropertyNames((3,4),('property3','property4'))
+ expected_summaries = []
+ expected_summaries.append( ('property3', 3, pythoncom.VT_BSTR))
+ expected_summaries.append( ('property4', 4, pythoncom.VT_BSTR))
+ psuser=None
+
+ pssum=pss.Create(pythoncom.FMTID_SummaryInformation,
+ pythoncom.IID_IPropertySetStorage,
+ storagecon.PROPSETFLAG_DEFAULT,
+ storagecon.STGM_READWRITE|storagecon.STGM_CREATE|storagecon.STGM_SHARE_EXCLUSIVE)
+ pssum.WriteMultiple((storagecon.PIDSI_AUTHOR,storagecon.PIDSI_COMMENTS),('me', 'comment'))
+
+ pssum=None
+ pss=None ## doesn't seem to be a close or release method, and you can't even reopen it from the same process until previous object is gone
+
+ pssread=pythoncom.StgOpenStorageEx(fname, storagecon.STGM_READ|storagecon.STGM_SHARE_EXCLUSIVE, storagecon.STGFMT_FILE, 0 , pythoncom.IID_IPropertySetStorage)
+ found_summaries = []
+ for psstat in pssread:
+ ps=pssread.Open(psstat[0],storagecon.STGM_READ|storagecon.STGM_SHARE_EXCLUSIVE)
+ for p in ps:
+ p_val = ps.ReadMultiple((p[1],))[0]
+ if (p[1]==storagecon.PIDSI_AUTHOR and p_val=='me') or \
+ (p[1]==storagecon.PIDSI_COMMENTS and p_val=='comment'):
+ pass
+ else:
+ self.fail("Uxexpected property %s/%s" % (p, p_val))
+ ps=None
+ ## FMTID_UserDefinedProperties can't exist without FMTID_DocSummaryInformation, and isn't returned independently from Enum
+ ## also can't be open at same time
+ if psstat[0]==pythoncom.FMTID_DocSummaryInformation:
+ ps=pssread.Open(pythoncom.FMTID_UserDefinedProperties,storagecon.STGM_READ|storagecon.STGM_SHARE_EXCLUSIVE)
+ for p in ps:
+ found_summaries.append(p)
+ ps=None
+ psread=None
+ expected_summaries.sort()
+ found_summaries.sort()
+ self.assertEqual(expected_summaries, found_summaries)
+
+if __name__=='__main__':
+ unittest.main()
+
\ No newline at end of file
diff --git a/Lib/site-packages/win32com/test/testStreams.py b/Lib/site-packages/win32com/test/testStreams.py
new file mode 100644
index 0000000..c477c0d
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testStreams.py
@@ -0,0 +1,131 @@
+import pythoncom
+import win32com.server.util
+import win32com.test.util
+
+import unittest
+from pywin32_testutil import str2bytes
+
+class Persists:
+ _public_methods_ = [ 'GetClassID', 'IsDirty', 'Load', 'Save',
+ 'GetSizeMax', 'InitNew' ]
+ _com_interfaces_ = [ pythoncom.IID_IPersistStreamInit ]
+ def __init__(self):
+ self.data = str2bytes("abcdefg")
+ self.dirty = 1
+ def GetClassID(self):
+ return pythoncom.IID_NULL
+ def IsDirty(self):
+ return self.dirty
+ def Load(self, stream):
+ self.data = stream.Read(26)
+ def Save(self, stream, clearDirty):
+ stream.Write(self.data)
+ if clearDirty:
+ self.dirty = 0
+ def GetSizeMax(self):
+ return 1024
+
+ def InitNew(self):
+ pass
+
+
+class Stream:
+ _public_methods_ = [ 'Read', 'Write', 'Seek' ]
+ _com_interfaces_ = [ pythoncom.IID_IStream ]
+
+ def __init__(self, data):
+ self.data = data
+ self.index = 0
+
+ def Read(self, amount):
+ result = self.data[self.index : self.index + amount]
+ self.index = self.index + amount
+ return result
+
+ def Write(self, data):
+ self.data = data
+ self.index = 0
+ return len(data)
+
+ def Seek(self, dist, origin):
+ if origin==pythoncom.STREAM_SEEK_SET:
+ self.index = dist
+ elif origin==pythoncom.STREAM_SEEK_CUR:
+ self.index = self.index + dist
+ elif origin==pythoncom.STREAM_SEEK_END:
+ self.index = len(self.data)+dist
+ else:
+ raise ValueError('Unknown Seek type: ' +str(origin))
+ if self.index < 0:
+ self.index = 0
+ else:
+ self.index = min(self.index, len(self.data))
+ return self.index
+
+class BadStream(Stream):
+ """ PyGStream::Read could formerly overflow buffer if the python implementation
+ returned more data than requested.
+ """
+ def Read(self, amount):
+ return str2bytes('x')*(amount+1)
+
+class StreamTest(win32com.test.util.TestCase):
+ def _readWrite(self, data, write_stream, read_stream = None):
+ if read_stream is None: read_stream = write_stream
+ write_stream.Write(data)
+ read_stream.Seek(0, pythoncom.STREAM_SEEK_SET)
+ got = read_stream.Read(len(data))
+ self.assertEqual(data, got)
+ read_stream.Seek(1, pythoncom.STREAM_SEEK_SET)
+ got = read_stream.Read(len(data)-2)
+ self.assertEqual(data[1:-1], got)
+
+ def testit(self):
+ mydata = str2bytes('abcdefghijklmnopqrstuvwxyz')
+
+ # First test the objects just as Python objects...
+ s = Stream(mydata)
+ p = Persists()
+
+ p.Load(s)
+ p.Save(s, 0)
+ self.assertEqual(s.data, mydata)
+
+ # Wrap the Python objects as COM objects, and make the calls as if
+ # they were non-Python COM objects.
+ s2 = win32com.server.util.wrap(s, pythoncom.IID_IStream)
+ p2 = win32com.server.util.wrap(p, pythoncom.IID_IPersistStreamInit)
+
+ self._readWrite(mydata, s, s)
+ self._readWrite(mydata, s, s2)
+ self._readWrite(mydata, s2, s)
+ self._readWrite(mydata, s2, s2)
+
+ self._readWrite(str2bytes("string with\0a NULL"), s2, s2)
+ # reset the stream
+ s.Write(mydata)
+ p2.Load(s2)
+ p2.Save(s2, 0)
+ self.assertEqual(s.data, mydata)
+
+ def testseek(self):
+ s = Stream(str2bytes('yo'))
+ s = win32com.server.util.wrap(s, pythoncom.IID_IStream)
+ # we used to die in py3k passing a value > 32bits
+ s.Seek(0x100000000, pythoncom.STREAM_SEEK_SET)
+
+ def testerrors(self):
+ # setup a test logger to capture tracebacks etc.
+ records, old_log = win32com.test.util.setup_test_logger()
+ ## check for buffer overflow in Read method
+ badstream = BadStream('Check for buffer overflow')
+ badstream2 = win32com.server.util.wrap(badstream, pythoncom.IID_IStream)
+ self.assertRaises(pythoncom.com_error, badstream2.Read, 10)
+ win32com.test.util.restore_test_logger(old_log)
+ # expecting 2 pythoncom errors to have been raised by the gateways.
+ self.assertEqual(len(records), 2)
+ self.failUnless(records[0].msg.startswith('pythoncom error'))
+ self.failUnless(records[1].msg.startswith('pythoncom error'))
+
+if __name__=='__main__':
+ unittest.main()
diff --git a/Lib/site-packages/win32com/test/testWMI.py b/Lib/site-packages/win32com/test/testWMI.py
new file mode 100644
index 0000000..d3edcc1
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testWMI.py
@@ -0,0 +1,17 @@
+from win32com.client import GetObject
+import win32com.test.util
+
+import unittest
+
+class Simple(win32com.test.util.TestCase):
+ def testit(self):
+ cses = GetObject("WinMgMts:").InstancesOf("Win32_Process")
+ vals = []
+ for cs in cses:
+ val = cs.Properties_("Caption").Value
+ vals.append(val)
+ self.failIf(len(vals)<5, "We only found %d processes!" % len(vals))
+
+if __name__=='__main__':
+ unittest.main()
+
diff --git a/Lib/site-packages/win32com/test/testall.py b/Lib/site-packages/win32com/test/testall.py
new file mode 100644
index 0000000..4844cce
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testall.py
@@ -0,0 +1,263 @@
+import sys, os, string, re
+import pythoncom
+import win32com.client
+from win32com.test.util import CheckClean, TestCase, \
+ CapturingFunctionTestCase, ShellTestCase, \
+ TestLoader, TestRunner, RegisterPythonServer
+import traceback
+import getopt
+
+import unittest
+
+verbosity = 1 # default unittest verbosity.
+
+try:
+ this_file = __file__
+except NameError:
+ this_file = sys.argv[0]
+
+def GenerateAndRunOldStyle():
+ from . import GenTestScripts
+ GenTestScripts.GenerateAll()
+ try:
+ pass #
+ finally:
+ GenTestScripts.CleanAll()
+
+def CleanGenerated():
+ import win32com, shutil
+ if os.path.isdir(win32com.__gen_path__):
+ if verbosity > 1:
+ print("Deleting files from %s" % (win32com.__gen_path__))
+ shutil.rmtree(win32com.__gen_path__)
+ import win32com.client.gencache
+ win32com.client.gencache.__init__() # Reset
+
+def RemoveRefCountOutput(data):
+ while 1:
+ last_line_pos = data.rfind("\n")
+ if not re.match("\[\d+ refs\]", data[last_line_pos+1:]):
+ break
+ if last_line_pos < 0:
+ # All the output
+ return ''
+ data = data[:last_line_pos]
+
+ return data
+
+def ExecuteSilentlyIfOK(cmd, testcase):
+ f = os.popen(cmd)
+ data = f.read().strip()
+ rc = f.close()
+ if rc:
+ print(data)
+ testcase.fail("Executing '%s' failed (%d)" % (cmd, rc))
+ # for "_d" builds, strip the '[xxx refs]' line
+ return RemoveRefCountOutput(data)
+
+class PyCOMTest(TestCase):
+ no_leak_tests = True # done by the test itself
+ def testit(self):
+ # Check that the item is registered, so we get the correct
+ # 'skipped' behaviour (and recorded as such) rather than either
+ # error or silence due to non-registration.
+ RegisterPythonServer(os.path.join(os.path.dirname(__file__), '..', "servers", "test_pycomtest.py"),
+ "Python.Test.PyCOMTest")
+
+ # Execute testPyComTest in its own process so it can play
+ # with the Python thread state
+ fname = os.path.join(os.path.dirname(this_file), "testPyComTest.py")
+ cmd = '%s "%s" -q 2>&1' % (sys.executable, fname)
+ data = ExecuteSilentlyIfOK(cmd, self)
+
+class PippoTest(TestCase):
+ def testit(self):
+ # Check we are registered before spawning the process.
+ from win32com.test import pippo_server
+ RegisterPythonServer(pippo_server.__file__, "Python.Test.Pippo")
+
+ python = sys.executable
+ fname = os.path.join(os.path.dirname(this_file), "testPippo.py")
+ cmd = '%s "%s" 2>&1' % (python, fname)
+ ExecuteSilentlyIfOK(cmd, self)
+
+# This is a list of "win32com.test.???" module names, optionally with a
+# function in that module if the module isn't unitest based...
+unittest_modules = [
+ # Level 1 tests.
+ """testIterators testvbscript_regexp testStorage
+ testStreams testWMI policySemantics testShell testROT
+ testAXScript testxslt testDictionary testCollections
+ testServers errorSemantics.test testvb testArrays
+ testClipboard testMarshal
+ """.split(),
+ # Level 2 tests.
+ """testMSOffice.TestAll testMSOfficeEvents.test testAccess.test
+ testExplorer.TestAll testExchange.test
+ """.split(),
+ # Level 3 tests.
+ """testmakepy.TestAll
+ """.split()
+]
+
+# A list of other unittest modules we use - these are fully qualified module
+# names and the module is assumed to be unittest based.
+unittest_other_modules = [
+ # Level 1 tests.
+ """win32com.directsound.test.ds_test
+ """.split(),
+ # Level 2 tests.
+ [],
+ # Level 3 tests.
+ []
+]
+
+
+output_checked_programs = [
+ # Level 1 tests.
+ [
+ ("cscript.exe /nologo //E:vbscript testInterp.vbs", "VBScript test worked OK"),
+ ("cscript.exe /nologo //E:vbscript testDictionary.vbs",
+ "VBScript has successfully tested Python.Dictionary"),
+ ],
+ # Level 2 tests.
+ [
+ ],
+ # Level 3 tests
+ [
+ ],
+]
+
+custom_test_cases = [
+ # Level 1 tests.
+ [
+ PyCOMTest,
+ PippoTest,
+ ],
+ # Level 2 tests.
+ [
+ ],
+ # Level 3 tests
+ [
+ ],
+]
+
+def get_test_mod_and_func(test_name, import_failures):
+ if test_name.find(".")>0:
+ mod_name, func_name = test_name.split(".")
+ else:
+ mod_name = test_name
+ func_name = None
+ fq_mod_name = "win32com.test." + mod_name
+ try:
+ __import__(fq_mod_name)
+ mod = sys.modules[fq_mod_name]
+ except:
+ import_failures.append((mod_name, sys.exc_info()[:2]))
+ return None, None
+ if func_name is None:
+ func = None
+ else:
+ func = getattr(mod, func_name)
+ return mod, func
+
+# Return a test suite all loaded with the tests we want to run
+def make_test_suite(test_level = 1):
+ suite = unittest.TestSuite()
+ import_failures = []
+ loader = TestLoader()
+ for i in range(testLevel):
+ for mod_name in unittest_modules[i]:
+ mod, func = get_test_mod_and_func(mod_name, import_failures)
+ if mod is None:
+ continue
+ if func is not None:
+ test = CapturingFunctionTestCase(func, description=mod_name)
+ else:
+ if hasattr(mod, "suite"):
+ test = mod.suite()
+ else:
+ test = loader.loadTestsFromModule(mod)
+ assert test.countTestCases() > 0, "No tests loaded from %r" % mod
+ suite.addTest(test)
+ for cmd, output in output_checked_programs[i]:
+ suite.addTest(ShellTestCase(cmd, output))
+
+ for test_class in custom_test_cases[i]:
+ suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(test_class))
+ # other "normal" unittest modules.
+ for i in range(testLevel):
+ for mod_name in unittest_other_modules[i]:
+ try:
+ __import__(mod_name)
+ except:
+ import_failures.append((mod_name, sys.exc_info()[:2]))
+ continue
+
+ mod = sys.modules[mod_name]
+ if hasattr(mod, "suite"):
+ test = mod.suite()
+ else:
+ test = loader.loadTestsFromModule(mod)
+ assert test.countTestCases() > 0, "No tests loaded from %r" % mod
+ suite.addTest(test)
+
+ return suite, import_failures
+
+def usage(why):
+ print(why)
+ print()
+ print("win32com test suite")
+ print("usage: testall [-v] test_level")
+ print(" where test_level is an integer 1-3. Level 1 tests are quick,")
+ print(" level 2 tests invoke Word, IE etc, level 3 take ages!")
+ sys.exit(1)
+
+if __name__=='__main__':
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "v")
+ except getopt.error as why:
+ usage(why)
+ for opt, val in opts:
+ if opt=='-v':
+ verbosity += 1
+ testLevel = 1 # default to quick test
+ test_names = []
+ for arg in args:
+ try:
+ testLevel = int(arg)
+ if testLevel < 0 or testLevel > 3:
+ raise ValueError("Only levels 1-3 are supported")
+ except ValueError:
+ test_names.append(arg)
+ if test_names:
+ usage("Test names are not supported yet")
+ CleanGenerated()
+
+ suite, import_failures = make_test_suite(testLevel)
+ if verbosity:
+ if hasattr(sys, "gettotalrefcount"):
+ print("This is a debug build - memory leak tests will also be run.")
+ print("These tests may take *many* minutes to run - be patient!")
+ print("(running from python.exe will avoid these leak tests)")
+ print("Executing level %d tests - %d test cases will be run" \
+ % (testLevel, suite.countTestCases()))
+ if verbosity==1 and suite.countTestCases() < 70:
+ # A little row of markers so the dots show how close to finished
+ print('|' * suite.countTestCases())
+ testRunner = TestRunner(verbosity=verbosity)
+ testResult = testRunner.run(suite)
+ if import_failures:
+ testResult.stream.writeln("*** The following test modules could not be imported ***")
+ for mod_name, (exc_type, exc_val) in import_failures:
+ desc = '\n'.join(traceback.format_exception_only(exc_type, exc_val))
+ testResult.stream.write("%s: %s" % (mod_name, desc))
+ testResult.stream.writeln("*** %d test(s) could not be run ***" % len(import_failures))
+
+ # re-print unit-test error here so it is noticed
+ if not testResult.wasSuccessful():
+ print("*" * 20, "- unittest tests FAILED")
+
+ CheckClean()
+ pythoncom.CoUninitialize()
+ CleanGenerated()
diff --git a/Lib/site-packages/win32com/test/testmakepy.py b/Lib/site-packages/win32com/test/testmakepy.py
new file mode 100644
index 0000000..406af33
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testmakepy.py
@@ -0,0 +1,53 @@
+# Test makepy - try and run it over every OCX in the windows system directory.
+
+import sys
+import win32api
+import traceback
+import glob
+import os
+import traceback
+
+import win32com.test.util
+from win32com.client import makepy, selecttlb, gencache
+import pythoncom
+import winerror
+
+def TestBuildAll(verbose = 1):
+ num = 0
+ tlbInfos = selecttlb.EnumTlbs()
+ for info in tlbInfos:
+ if verbose:
+ print("%s (%s)" % (info.desc, info.dll))
+ try:
+ makepy.GenerateFromTypeLibSpec(info)
+# sys.stderr.write("Attr typeflags for coclass referenced object %s=%d (%d), typekind=%d\n" % (name, refAttr.wTypeFlags, refAttr.wTypeFlags & pythoncom.TYPEFLAG_FDUAL,refAttr.typekind))
+ num += 1
+ except pythoncom.com_error as details:
+ # Ignore these 2 errors, as the are very common and can obscure
+ # useful warnings.
+ if details.hresult not in [winerror.TYPE_E_CANTLOADLIBRARY,
+ winerror.TYPE_E_LIBNOTREGISTERED]:
+ print("** COM error on", info.desc)
+ print(details)
+ except KeyboardInterrupt:
+ print("Interrupted!")
+ raise KeyboardInterrupt
+ except:
+ print("Failed:", info.desc)
+ traceback.print_exc()
+ if makepy.bForDemandDefault:
+ # This only builds enums etc by default - build each
+ # interface manually
+ tinfo = (info.clsid, info.lcid, info.major, info.minor)
+ mod = gencache.EnsureModule(info.clsid, info.lcid, info.major, info.minor)
+ for name in mod.NamesToIIDMap.keys():
+ makepy.GenerateChildFromTypeLibSpec(name, tinfo)
+ return num
+
+def TestAll(verbose = 0):
+ num = TestBuildAll(verbose)
+ print("Generated and imported", num, "modules")
+ win32com.test.util.CheckClean()
+
+if __name__=='__main__':
+ TestAll("-q" not in sys.argv)
diff --git a/Lib/site-packages/win32com/test/testvb.py b/Lib/site-packages/win32com/test/testvb.py
new file mode 100644
index 0000000..ebc7534
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testvb.py
@@ -0,0 +1,510 @@
+# Test code for a VB Program.
+#
+# This requires the PythonCOM VB Test Harness.
+#
+
+import sys
+import winerror
+import pythoncom, win32com.client, win32com.client.dynamic, win32com.client.gencache
+from win32com.server.util import NewCollection, wrap
+from win32com.test import util
+from pywin32_testutil import str2memory
+
+import traceback
+
+# for debugging
+useDispatcher = None
+## import win32com.server.dispatcher
+## useDispatcher = win32com.server.dispatcher.DefaultDebugDispatcher
+
+error = RuntimeError
+
+# Set up a COM object that VB will do some callbacks on. This is used
+# to test byref params for gateway IDispatch.
+class TestObject:
+ _public_methods_ = ["CallbackVoidOneByRef","CallbackResultOneByRef", "CallbackVoidTwoByRef",
+ "CallbackString","CallbackResultOneByRefButReturnNone",
+ "CallbackVoidOneByRefButReturnNone",
+ "CallbackArrayResult", "CallbackArrayResultOneArrayByRef",
+ "CallbackArrayResultWrongSize"
+ ]
+ def CallbackVoidOneByRef(self, intVal):
+ return intVal + 1
+ def CallbackResultOneByRef(self, intVal):
+ return intVal, intVal + 1
+ def CallbackVoidTwoByRef(self, int1, int2):
+ return int1+int2, int1-int2
+ def CallbackString(self, strVal):
+ return 0, strVal + " has visited Python"
+ def CallbackArrayResult(self, arrayVal):
+ ret = []
+ for i in arrayVal:
+ ret.append(i+1)
+ # returning as a list forces it be processed as a single result
+ # (rather than a tuple, where it may be interpreted as
+ # multiple results for byref unpacking)
+ return ret
+ def CallbackArrayResultWrongSize(self, arrayVal):
+ return list(arrayVal[:-1])
+ def CallbackArrayResultOneArrayByRef(self, arrayVal):
+ ret = []
+ for i in arrayVal:
+ ret.append(i+1)
+ # See above for list processing.
+ return list(arrayVal), ret
+
+ def CallbackResultOneByRefButReturnNone(self, intVal):
+ return
+ def CallbackVoidOneByRefButReturnNone(self, intVal):
+ return
+
+def TestVB( vbtest, bUseGenerated ):
+ vbtest.LongProperty = -1
+ if vbtest.LongProperty != -1:
+ raise error("Could not set the long property correctly.")
+ vbtest.IntProperty = 10
+ if vbtest.IntProperty != 10:
+ raise error("Could not set the integer property correctly.")
+ vbtest.VariantProperty = 10
+ if vbtest.VariantProperty != 10:
+ raise error("Could not set the variant integer property correctly.")
+ vbtest.VariantProperty = str2memory('raw\0data')
+ if vbtest.VariantProperty != str2memory('raw\0data'):
+ raise error("Could not set the variant buffer property correctly.")
+ vbtest.StringProperty = "Hello from Python"
+ if vbtest.StringProperty != "Hello from Python":
+ raise error("Could not set the string property correctly.")
+ vbtest.VariantProperty = "Hello from Python"
+ if vbtest.VariantProperty != "Hello from Python":
+ raise error("Could not set the variant string property correctly.")
+ vbtest.VariantProperty = (1.0, 2.0, 3.0)
+ if vbtest.VariantProperty != (1.0, 2.0, 3.0):
+ raise error("Could not set the variant property to an array of floats correctly - '%s'." % (vbtest.VariantProperty,))
+
+ TestArrays(vbtest, bUseGenerated)
+ TestStructs(vbtest)
+ TestCollections(vbtest)
+
+ assert vbtest.TakeByValObject(vbtest)==vbtest
+
+ # Python doesnt support PUTREF properties without a typeref
+ # (although we could)
+ if bUseGenerated:
+ ob = vbtest.TakeByRefObject(vbtest)
+ assert ob[0]==vbtest and ob[1]==vbtest
+
+ # A property that only has PUTREF defined.
+ vbtest.VariantPutref = vbtest
+ if vbtest.VariantPutref._oleobj_!= vbtest._oleobj_:
+ raise error("Could not set the VariantPutref property correctly.")
+ # Cant test further types for this VariantPutref, as only
+ # COM objects can be stored ByRef.
+
+ # A "set" type property - only works for generated.
+ # VB recognizes a collection via a few "private" interfaces that we
+ # could later build support in for.
+# vbtest.CollectionProperty = NewCollection((1,2,"3", "Four"))
+# if vbtest.CollectionProperty != (1,2,"3", "Four"):
+# raise error("Could not set the Collection property correctly - got back " + str(vbtest.CollectionProperty))
+
+ # These are sub's that have a single byref param
+ # Result should be just the byref.
+ if vbtest.IncrementIntegerParam(1) != 2:
+ raise error("Could not pass an integer byref")
+
+# Sigh - we cant have *both* "ommited byref" and optional args
+# We really have to opt that args nominated as optional work as optional
+# rather than simply all byrefs working as optional.
+# if vbtest.IncrementIntegerParam() != 1:
+# raise error("Could not pass an omitted integer byref")
+
+ if vbtest.IncrementVariantParam(1) != 2:
+ raise error("Could not pass an int VARIANT byref:"+str(vbtest.IncrementVariantParam(1)))
+
+ if vbtest.IncrementVariantParam(1.5) != 2.5:
+ raise error("Could not pass a float VARIANT byref")
+
+ # Can't test IncrementVariantParam with the param omitted as it
+ # it not declared in the VB code as "Optional"
+ callback_ob = wrap(TestObject(), useDispatcher = useDispatcher)
+ vbtest.DoSomeCallbacks(callback_ob)
+
+ ret = vbtest.PassIntByVal(1)
+ if ret != 2:
+ raise error("Could not increment the integer - "+str(ret))
+
+ TestVBInterface(vbtest)
+ # Python doesnt support byrefs without some sort of generated support.
+ if bUseGenerated:
+ # This is a VB function that takes a single byref
+ # Hence 2 return values - function and byref.
+ ret = vbtest.PassIntByRef(1)
+ if ret != (1,2):
+ raise error("Could not increment the integer - "+str(ret))
+ # Check you can leave a byref arg blank.
+# see above
+# ret = vbtest.PassIntByRef()
+# if ret != (0,1):
+# raise error("Could not increment the integer with default arg- "+str(ret))
+
+def _DoTestCollection(vbtest, col_name, expected):
+ # It sucks that some objects allow "Count()", but others "Count"
+ def _getcount(ob):
+ r = getattr(ob, "Count")
+ if type(r)!=type(0):
+ return r()
+ return r
+ c = getattr(vbtest, col_name)
+ check = []
+ for item in c:
+ check.append(item)
+ if check != list(expected):
+ raise error("Collection %s didn't have %r (had %r)" % (col_name, expected, check))
+ # Just looping over the collection again works (ie, is restartable)
+ check = []
+ for item in c:
+ check.append(item)
+ if check != list(expected):
+ raise error("Collection 2nd time around %s didn't have %r (had %r)" % (col_name, expected, check))
+ # Check we can get it via iter()
+ i = iter(getattr(vbtest, col_name))
+ check = []
+ for item in i:
+ check.append(item)
+ if check != list(expected):
+ raise error("Collection iterator %s didn't have %r 2nd time around (had %r)" % (col_name, expected, check))
+ # but an iterator is not restartable
+ check = []
+ for item in i:
+ check.append(item)
+ if check != []:
+ raise error("2nd time around Collection iterator %s wasn't empty (had %r)" % (col_name, check))
+
+ # Check len()==Count()
+ c = getattr(vbtest, col_name)
+ if len(c) != _getcount(c):
+ raise error("Collection %s __len__(%r) wasn't==Count(%r)" % (col_name, len(c), _getcount(c)))
+ # Check we can do it with zero based indexing.
+ c = getattr(vbtest, col_name)
+ check = []
+ for i in range(_getcount(c)):
+ check.append(c[i])
+ if check != list(expected):
+ raise error("Collection %s didn't have %r (had %r)" % (col_name, expected, check))
+
+ # Check we can do it with our old "Skip/Next" methods.
+ c = getattr(vbtest, col_name)._NewEnum()
+ check = []
+ while 1:
+ n = c.Next()
+ if not n:
+ break
+ check.append(n[0])
+ if check != list(expected):
+ raise error("Collection %s didn't have %r (had %r)" % (col_name, expected, check))
+
+def TestCollections(vbtest):
+ _DoTestCollection(vbtest, "CollectionProperty", [1,"Two", "3"])
+ # zero based indexing works for simple VB collections.
+ if vbtest.CollectionProperty[0] != 1:
+ raise error("The CollectionProperty[0] element was not the default value")
+
+ _DoTestCollection(vbtest, "EnumerableCollectionProperty", [])
+ vbtest.EnumerableCollectionProperty.Add(1)
+ vbtest.EnumerableCollectionProperty.Add("Two")
+ vbtest.EnumerableCollectionProperty.Add("3")
+ _DoTestCollection(vbtest, "EnumerableCollectionProperty", [1,"Two", "3"])
+
+def _DoTestArray(vbtest, data, expected_exception = None):
+ try:
+ vbtest.ArrayProperty = data
+ if expected_exception is not None:
+ raise error("Expected '%s'" % expected_exception)
+ except expected_exception:
+ return
+ got = vbtest.ArrayProperty
+ if got != data:
+ raise error(
+ "Could not set the array data correctly - got %r, expected %r"
+ % (got, data))
+
+def TestArrays(vbtest, bUseGenerated):
+ # Try and use a safe array (note that the VB code has this declared as a VARIANT
+ # and I cant work out how to force it to use native arrays!
+ # (NOTE Python will convert incoming arrays to tuples, so we pass a tuple, even tho
+ # a list works fine - just makes it easier for us to compare the result!
+ # Empty array
+ _DoTestArray(vbtest, ())
+ # Empty child array
+ _DoTestArray(vbtest, ((), ()))
+ # ints
+ _DoTestArray(vbtest, tuple(range(1,100)))
+ # Floats
+ _DoTestArray(vbtest, (1.0, 2.0, 3.0))
+ # Strings.
+ _DoTestArray(vbtest, tuple("Hello from Python".split()))
+ # Date and Time?
+ # COM objects.
+ _DoTestArray(vbtest, (vbtest, vbtest))
+ # Mixed
+ _DoTestArray(vbtest, (1, 2.0, "3"))
+ # Array alements containing other arrays
+ _DoTestArray(vbtest, (1,(vbtest, vbtest),("3","4")))
+ # Multi-dimensional
+ _DoTestArray(vbtest, (( (1,2,3), (4,5,6) )))
+ _DoTestArray(vbtest, (( (vbtest,vbtest,vbtest), (vbtest,vbtest,vbtest) )))
+ # Another dimension!
+ arrayData = ( ((1,2),(3,4),(5,6)), ((7,8),(9,10),(11,12)) )
+ arrayData = ( ((vbtest,vbtest),(vbtest,vbtest),(vbtest,vbtest)),
+ ((vbtest,vbtest),(vbtest,vbtest),(vbtest,vbtest)) )
+ _DoTestArray(vbtest, arrayData)
+
+ # Check that when a '__getitem__ that fails' object is the first item
+ # in the structure, we don't mistake it for a sequence.
+ _DoTestArray(vbtest, (vbtest, 2.0, "3"))
+ _DoTestArray(vbtest, (1, 2.0, vbtest))
+
+ # Pass arbitrarily sized arrays - these used to fail, but thanks to
+ # Stefan Schukat, they now work!
+ expected_exception = None
+ arrayData = ( ((1,2,1),(3,4),(5,6)), ((7,8),(9,10),(11,12)) )
+ _DoTestArray(vbtest, arrayData, expected_exception)
+ arrayData = ( ((vbtest,vbtest),), ((vbtest,),))
+ _DoTestArray(vbtest, arrayData, expected_exception)
+ # Pass bad data - last item wrong size
+ arrayData = ( ((1,2),(3,4),(5,6,8)), ((7,8),(9,10),(11,12)) )
+ _DoTestArray(vbtest, arrayData, expected_exception)
+
+ # byref safearray results with incorrect size.
+ callback_ob = wrap(TestObject(), useDispatcher = useDispatcher)
+ print("** Expecting a 'ValueError' exception to be printed next:")
+ try:
+ vbtest.DoCallbackSafeArraySizeFail(callback_ob)
+ except pythoncom.com_error as exc:
+ assert exc.excepinfo[1] == "Python COM Server Internal Error", "Didnt get the correct exception - '%s'" % (exc,)
+
+ if bUseGenerated:
+ # This one is a bit strange! The array param is "ByRef", as VB insists.
+ # The function itself also _returns_ the arram param.
+ # Therefore, Python sees _2_ result values - one for the result,
+ # and one for the byref.
+ testData = "Mark was here".split()
+ resultData, byRefParam = vbtest.PassSAFEARRAY(testData)
+ if testData != list(resultData):
+ raise error("The safe array data was not what we expected - got " + str(resultData))
+ if testData != list(byRefParam):
+ raise error("The safe array data was not what we expected - got " + str(byRefParam))
+ testData = [1.0, 2.0, 3.0]
+ resultData, byRefParam = vbtest.PassSAFEARRAYVariant(testData)
+ assert testData == list(byRefParam)
+ assert testData == list(resultData)
+ testData = ["hi", "from", "Python"]
+ resultData, byRefParam = vbtest.PassSAFEARRAYVariant(testData)
+ assert testData == list(byRefParam), "Expected '%s', got '%s'" % (testData, list(byRefParam))
+ assert testData == list(resultData), "Expected '%s', got '%s'" % (testData, list(resultData))
+ # This time, instead of an explicit str() for 1.5, we just
+ # pass Unicode, so the result should compare equal
+ testData = [1, 2.0, "3"]
+ resultData, byRefParam = vbtest.PassSAFEARRAYVariant(testData)
+ assert testData == list(byRefParam)
+ assert testData == list(resultData)
+ print("Array tests passed")
+
+def TestStructs(vbtest):
+ try:
+ vbtest.IntProperty = "One"
+ raise error("Should have failed by now")
+ except pythoncom.com_error as exc:
+ if exc.hresult != winerror.DISP_E_TYPEMISMATCH:
+ raise error("Expected DISP_E_TYPEMISMATCH")
+
+ s = vbtest.StructProperty
+ if s.int_val != 99 or str(s.str_val) != "hello":
+ raise error("The struct value was not correct")
+ s.str_val = "Hi from Python"
+ s.int_val = 11
+ if s.int_val != 11 or str(s.str_val) != "Hi from Python":
+ raise error("The struct value didnt persist!")
+
+ if s.sub_val.int_val != 66 or str(s.sub_val.str_val) != "sub hello":
+ raise error("The sub-struct value was not correct")
+ sub = s.sub_val
+ sub.int_val = 22
+ if sub.int_val != 22:
+ print(sub.int_val)
+ raise error("The sub-struct value didnt persist!")
+
+ if s.sub_val.int_val != 22:
+ print(s.sub_val.int_val)
+ raise error("The sub-struct value (re-fetched) didnt persist!")
+
+ if s.sub_val.array_val[0].int_val != 0 or str(s.sub_val.array_val[0].str_val) != "zero":
+ print(s.sub_val.array_val[0].int_val)
+ raise error("The array element wasnt correct")
+ s.sub_val.array_val[0].int_val = 99
+ s.sub_val.array_val[1].int_val = 66
+ if s.sub_val.array_val[0].int_val != 99 or \
+ s.sub_val.array_val[1].int_val != 66:
+ print(s.sub_val.array_val[0].int_val)
+ raise error("The array element didnt persist.")
+ # Now pass the struct back to VB
+ vbtest.StructProperty = s
+ # And get it back again
+ s = vbtest.StructProperty
+ if s.int_val != 11 or str(s.str_val) != "Hi from Python":
+ raise error("After sending to VB, the struct value didnt persist!")
+ if s.sub_val.array_val[0].int_val != 99:
+ raise error("After sending to VB, the struct array value didnt persist!")
+
+ # Now do some object equality tests.
+ assert s==s
+ assert s!=None
+ if sys.version_info > (3,0):
+ try:
+ s < None
+ raise error("Expected type error")
+ except TypeError:
+ pass
+ try:
+ None < s
+ raise error("Expected type error")
+ except TypeError:
+ pass
+ assert s != s.sub_val
+ import copy
+ s2 = copy.copy(s)
+ assert s is not s2
+ assert s == s2
+ s2.int_val = 123
+ assert s != s2
+ # Make sure everything works with functions
+ s2 = vbtest.GetStructFunc()
+ assert s==s2
+ vbtest.SetStructSub(s2)
+
+ # Create a new structure, and set its elements.
+ s = win32com.client.Record("VBStruct", vbtest)
+ assert s.int_val == 0, "new struct inst initialized correctly!"
+ s.int_val = -1
+ vbtest.SetStructSub(s)
+ assert vbtest.GetStructFunc().int_val == -1, "new struct didnt make the round trip!"
+ # Finally, test stand-alone structure arrays.
+ s_array = vbtest.StructArrayProperty
+ assert s_array is None, "Expected None from the uninitialized VB array"
+ vbtest.MakeStructArrayProperty(3)
+ s_array = vbtest.StructArrayProperty
+ assert len(s_array)==3
+ for i in range(len(s_array)):
+ assert s_array[i].int_val == i
+ assert s_array[i].sub_val.int_val == i
+ assert s_array[i].sub_val.array_val[0].int_val == i
+ assert s_array[i].sub_val.array_val[1].int_val == i+1
+ assert s_array[i].sub_val.array_val[2].int_val == i+2
+
+ # Some error type checks.
+ try:
+ s.bad_attribute
+ raise RuntimeError("Could get a bad attribute")
+ except AttributeError:
+ pass
+ m = s.__members__
+ assert m[0]=="int_val" and m[1]=="str_val" and m[2]=="ob_val" and m[3]=="sub_val", m
+
+ # Test attribute errors.
+ try:
+ s.foo
+ raise RuntimeError("Expected attribute error")
+ except AttributeError as exc:
+ assert "foo" in str(exc), exc
+
+ # test repr - it uses repr() of the sub-objects, so check it matches.
+ expected = "com_struct(int_val=%r, str_val=%r, ob_val=%r, sub_val=%r)" % (s.int_val, s.str_val, s.ob_val, s.sub_val)
+ if repr(s) != expected:
+ print("Expected repr:", expected)
+ print("Actual repr :", repr(s))
+ raise RuntimeError("repr() of record object failed")
+
+ print("Struct/Record tests passed")
+
+def TestVBInterface(ob):
+ t = ob.GetInterfaceTester(2)
+ if t.getn() != 2:
+ raise error("Initial value wrong")
+ t.setn(3)
+ if t.getn() != 3:
+ raise error("New value wrong")
+
+def TestObjectSemantics(ob):
+ # a convenient place to test some of our equality semantics
+ assert ob==ob._oleobj_
+ assert not ob!=ob._oleobj_
+ # same test again, but lhs and rhs reversed.
+ assert ob._oleobj_==ob
+ assert not ob._oleobj_!=ob
+ # same tests but against different pointers. COM identity rules should
+ # still ensure all works
+ assert ob._oleobj_==ob._oleobj_.QueryInterface(pythoncom.IID_IUnknown)
+ assert not ob._oleobj_!=ob._oleobj_.QueryInterface(pythoncom.IID_IUnknown)
+
+ assert ob._oleobj_!=None
+ assert None!=ob._oleobj_
+ assert ob!=None
+ assert None!=ob
+ if sys.version_info > (3,0):
+ try:
+ ob < None
+ raise error("Expected type error")
+ except TypeError:
+ pass
+ try:
+ None < ob
+ raise error("Expected type error")
+ except TypeError:
+ pass
+
+ assert ob._oleobj_.QueryInterface(pythoncom.IID_IUnknown)==ob._oleobj_
+ assert not ob._oleobj_.QueryInterface(pythoncom.IID_IUnknown)!=ob._oleobj_
+
+ assert ob._oleobj_==ob._oleobj_.QueryInterface(pythoncom.IID_IDispatch)
+ assert not ob._oleobj_!=ob._oleobj_.QueryInterface(pythoncom.IID_IDispatch)
+
+ assert ob._oleobj_.QueryInterface(pythoncom.IID_IDispatch)==ob._oleobj_
+ assert not ob._oleobj_.QueryInterface(pythoncom.IID_IDispatch)!=ob._oleobj_
+
+ print("Object semantic tests passed")
+
+def DoTestAll():
+ o = win32com.client.Dispatch("PyCOMVBTest.Tester")
+ TestObjectSemantics(o)
+ TestVB(o,1)
+
+ o = win32com.client.dynamic.DumbDispatch("PyCOMVBTest.Tester")
+ TestObjectSemantics(o)
+ TestVB(o,0)
+
+def TestAll():
+ # Import the type library for the test module. Let the 'invalid clsid'
+ # exception filter up, where the test runner will treat it as 'skipped'
+ win32com.client.gencache.EnsureDispatch("PyCOMVBTest.Tester")
+
+ if not __debug__:
+ raise RuntimeError("This must be run in debug mode - we use assert!")
+ try:
+ DoTestAll()
+ print("All tests appear to have worked!")
+ except:
+ # ?????
+ print("TestAll() failed!!")
+ traceback.print_exc()
+ raise
+
+# Make this test run under our test suite to leak tests etc work
+def suite():
+ import unittest
+ test = util.CapturingFunctionTestCase(TestAll, description="VB tests")
+ suite = unittest.TestSuite()
+ suite.addTest(test)
+ return suite
+
+if __name__=='__main__':
+ util.testmain()
diff --git a/Lib/site-packages/win32com/test/testvbscript_regexp.py b/Lib/site-packages/win32com/test/testvbscript_regexp.py
new file mode 100644
index 0000000..6d5f458
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testvbscript_regexp.py
@@ -0,0 +1,37 @@
+import unittest
+from win32com.client.gencache import EnsureDispatch
+from win32com.client.dynamic import DumbDispatch
+import win32com.test.util
+
+class RegexTest(win32com.test.util.TestCase):
+ def _CheckMatches(self, match, expected):
+ found = []
+ for imatch in match:
+ found.append(imatch.FirstIndex)
+ self.assertEquals(list(found), list(expected))
+
+ def _TestVBScriptRegex(self, re):
+ StringToSearch = "Python python pYthon Python"
+ re.Pattern = "Python"
+ re.Global = True
+
+ re.IgnoreCase = True
+ match = re.Execute(StringToSearch)
+ expected = 0, 7, 14, 21
+ self._CheckMatches(match, expected)
+
+ re.IgnoreCase = False
+ match = re.Execute(StringToSearch)
+ expected = 0, 21
+ self._CheckMatches(match, expected)
+
+ def testDynamic(self):
+ re = DumbDispatch("VBScript.Regexp")
+ self._TestVBScriptRegex(re)
+
+ def testGenerated(self):
+ re = EnsureDispatch("VBScript.Regexp")
+ self._TestVBScriptRegex(re)
+
+if __name__=='__main__':
+ unittest.main()
diff --git a/Lib/site-packages/win32com/test/testxslt.js b/Lib/site-packages/win32com/test/testxslt.js
new file mode 100644
index 0000000..fb824cd
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testxslt.js
@@ -0,0 +1,12 @@
+//Args: input-file style-file output-file
+var xml = WScript.CreateObject("Microsoft.XMLDOM"); //input
+xml.validateOnParse=false;
+xml.load(WScript.Arguments(0));
+var xsl = WScript.CreateObject("Microsoft.XMLDOM"); //style
+xsl.validateOnParse=false;
+xsl.load(WScript.Arguments(1));
+var out = WScript.CreateObject("Scripting.FileSystemObject"); //output
+var replace = true; var unicode = false; //output file properties
+var hdl = out.CreateTextFile( WScript.Arguments(2), replace, unicode )
+hdl.write( xml.transformNode( xsl.documentElement ));
+//eof
diff --git a/Lib/site-packages/win32com/test/testxslt.py b/Lib/site-packages/win32com/test/testxslt.py
new file mode 100644
index 0000000..a599051
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testxslt.py
@@ -0,0 +1,29 @@
+import os
+import tempfile
+import unittest
+import win32com.test.util
+
+expected_output = "The jscript test worked.\nThe Python test worked"
+
+class XSLT(win32com.test.util.TestCase):
+ def testAll(self):
+ output_name = tempfile.mktemp("-pycom-test")
+ cmd = "cscript //nologo testxslt.js doesnt_matter.xml testxslt.xsl " + output_name
+ win32com.test.util.ExecuteShellCommand(cmd, self)
+ try:
+ f=open(output_name)
+ try:
+ got = f.read()
+ if got != expected_output:
+ print("ERROR: XSLT expected output of %r" % (expected_output,))
+ print("but got %r" % (got,))
+ finally:
+ f.close()
+ finally:
+ try:
+ os.unlink(output_name)
+ except os.error:
+ pass
+
+if __name__=='__main__':
+ unittest.main()
diff --git a/Lib/site-packages/win32com/test/testxslt.xsl b/Lib/site-packages/win32com/test/testxslt.xsl
new file mode 100644
index 0000000..05e960d
--- /dev/null
+++ b/Lib/site-packages/win32com/test/testxslt.xsl
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+ function worked() {
+ return "The jscript test worked";
+ }
+
+
+
+
+def worked():
+ return "The Python test worked"
+
+
+
+
+
+.
+
+
+
+
+
+
diff --git a/Lib/site-packages/win32com/test/util.py b/Lib/site-packages/win32com/test/util.py
new file mode 100644
index 0000000..cf906a8
--- /dev/null
+++ b/Lib/site-packages/win32com/test/util.py
@@ -0,0 +1,229 @@
+import sys, os
+import win32api
+import tempfile
+import unittest
+import gc
+import pythoncom
+import winerror
+from pythoncom import _GetInterfaceCount, _GetGatewayCount
+import win32com
+import logging
+import winreg
+import io as StringIO
+
+import pywin32_testutil
+from pywin32_testutil import TestLoader, TestResult, TestRunner, LeakTestCase
+
+def CheckClean():
+ # Ensure no lingering exceptions - Python should have zero outstanding
+ # COM objects
+ try:
+ sys.exc_clear()
+ except AttributeError:
+ pass # py3k
+ c = _GetInterfaceCount()
+ if c:
+ print("Warning - %d com interface objects still alive" % c)
+ c = _GetGatewayCount()
+ if c:
+ print("Warning - %d com gateway objects still alive" % c)
+
+def RegisterPythonServer(filename, progids=None, verbose=0):
+ if progids:
+ if isinstance(progids, str):
+ progids = [progids]
+ # we know the CLSIDs we need, but we might not be an admin user
+ # and otherwise unable to register them. So as long as the progids
+ # exist and the DLL points at our version, assume it already is.
+ why_not = None
+ for progid in progids:
+ try:
+ clsid = pythoncom.MakeIID(progid)
+ except pythoncom.com_error:
+ # no progid - not registered.
+ break
+ # have a CLSID - open it.
+ try:
+ HKCR = winreg.HKEY_CLASSES_ROOT
+ hk = winreg.OpenKey(HKCR, "CLSID\\%s" % clsid)
+ dll = winreg.QueryValue(hk, "InprocServer32")
+ except WindowsError:
+ # no CLSID or InProcServer32 - not good!
+ break
+ ok_files = [os.path.basename(pythoncom.__file__),
+ 'pythoncomloader%d%d.dll' % (sys.version_info[0], sys.version_info[1])]
+ if os.path.basename(dll) not in ok_files:
+ why_not = "%r is registered against a different Python version (%s)" % (progid, dll)
+ break
+ else:
+ #print "Skipping registration of '%s' - already registered" % filename
+ return
+ # needs registration - see if its likely!
+ try:
+ from win32com.shell.shell import IsUserAnAdmin
+ except ImportError:
+ print("Can't import win32com.shell - no idea if you are an admin or not?")
+ is_admin = False
+ else:
+ try:
+ is_admin = IsUserAnAdmin()
+ except pythoncom.com_error:
+ # old, less-secure OS - assume *is* admin.
+ is_admin = True
+ if not is_admin:
+ msg = "%r isn't registered, but I'm not an administrator who can register it." % progids[0]
+ if why_not:
+ msg += "\n(registration check failed as %s)" % why_not
+ # throw a normal "class not registered" exception - we don't report
+ # them the same way as "real" errors.
+ raise pythoncom.com_error(winerror.CO_E_CLASSSTRING, msg, None, -1)
+ # so theoretically we are able to register it.
+ cmd = '%s "%s" --unattended > nul 2>&1' % (win32api.GetModuleFileName(0), filename)
+ if verbose:
+ print("Registering engine", filename)
+# print cmd
+ rc = os.system(cmd)
+ if rc:
+ print("Registration command was:")
+ print(cmd)
+ raise RuntimeError("Registration of engine '%s' failed" % filename)
+
+def ExecuteShellCommand(cmd, testcase,
+ expected_output = None, # Set to '' to check for nothing
+ tracebacks_ok = 0, # OK if the output contains a t/b?
+ ):
+ output_name = tempfile.mktemp('win32com_test')
+ cmd = cmd + ' > "%s" 2>&1' % output_name
+ rc = os.system(cmd)
+ output = open(output_name, "r").read().strip()
+ os.remove(output_name)
+ class Failed(Exception): pass
+ try:
+ if rc:
+ raise Failed("exit code was " + str(rc))
+ if expected_output is not None and output != expected_output:
+ raise Failed("Expected output %r (got %r)" % (expected_output, output))
+ if not tracebacks_ok and \
+ output.find("Traceback (most recent call last)")>=0:
+ raise Failed("traceback in program output")
+ return output
+ except Failed as why:
+ print("Failed to exec command '%r'" % cmd)
+ print("Failed as", why)
+ print("** start of program output **")
+ print(output)
+ print("** end of program output **")
+ testcase.fail("Executing '%s' failed as %s" % (cmd, why))
+
+def assertRaisesCOM_HRESULT(testcase, hresult, func, *args, **kw):
+ try:
+ func(*args, **kw)
+ except pythoncom.com_error as details:
+ if details.hresult==hresult:
+ return
+ testcase.fail("Excepected COM exception with HRESULT 0x%x" % hresult)
+
+class CaptureWriter:
+ def __init__(self):
+ self.old_err = self.old_out = None
+ self.clear()
+ def capture(self):
+ self.clear()
+ self.old_out = sys.stdout
+ self.old_err = sys.stderr
+ sys.stdout = sys.stderr = self
+ def release(self):
+ if self.old_out:
+ sys.stdout = self.old_out
+ self.old_out = None
+ if self.old_err:
+ sys.stderr = self.old_err
+ self.old_err = None
+ def clear(self):
+ self.captured = []
+ def write(self, msg):
+ self.captured.append(msg)
+ def get_captured(self):
+ return "".join(self.captured)
+ def get_num_lines_captured(self):
+ return len("".join(self.captured).split("\n"))
+
+
+# Utilities to set the win32com logger to something what just captures
+# records written and doesn't print them.
+class LogHandler(logging.Handler):
+ def __init__(self):
+ self.emitted = []
+ logging.Handler.__init__(self)
+ def emit(self, record):
+ self.emitted.append(record)
+
+_win32com_logger = None
+def setup_test_logger():
+ old_log = getattr(win32com, "logger", None)
+ global _win32com_logger
+ if _win32com_logger is None:
+ _win32com_logger = logging.Logger('test')
+ handler = LogHandler()
+ _win32com_logger.addHandler(handler)
+
+ win32com.logger = _win32com_logger
+ handler = _win32com_logger.handlers[0]
+ handler.emitted = []
+ return handler.emitted, old_log
+
+def restore_test_logger(prev_logger):
+ assert prev_logger is None, "who needs this?"
+ if prev_logger is None:
+ del win32com.logger
+ else:
+ win32com.logger = prev_logger
+
+# We used to override some of this (and may later!)
+TestCase = unittest.TestCase
+
+def CapturingFunctionTestCase(*args, **kw):
+ real_test = _CapturingFunctionTestCase(*args, **kw)
+ return LeakTestCase(real_test)
+
+class _CapturingFunctionTestCase(unittest.FunctionTestCase):#, TestCaseMixin):
+ def __call__(self, result=None):
+ if result is None: result = self.defaultTestResult()
+ writer = CaptureWriter()
+ #self._preTest()
+ writer.capture()
+ try:
+ unittest.FunctionTestCase.__call__(self, result)
+ if getattr(self, "do_leak_tests", 0) and hasattr(sys, "gettotalrefcount"):
+ self.run_leak_tests(result)
+ finally:
+ writer.release()
+ #self._postTest(result)
+ output = writer.get_captured()
+ self.checkOutput(output, result)
+ if result.showAll:
+ print(output)
+ def checkOutput(self, output, result):
+ if output.find("Traceback")>=0:
+ msg = "Test output contained a traceback\n---\n%s\n---" % output
+ result.errors.append((self, msg))
+
+class ShellTestCase(unittest.TestCase):
+ def __init__(self, cmd, expected_output):
+ self.__cmd = cmd
+ self.__eo = expected_output
+ unittest.TestCase.__init__(self)
+ def runTest(self):
+ ExecuteShellCommand(self.__cmd, self, self.__eo)
+ def __str__(self):
+ max = 30
+ if len(self.__cmd)>max:
+ cmd_repr = self.__cmd[:max] + "..."
+ else:
+ cmd_repr = self.__cmd
+ return "exec: " + cmd_repr
+
+
+def testmain(*args, **kw):
+ pywin32_testutil.testmain(*args, **kw)
+ CheckClean()
diff --git a/Lib/site-packages/win32com/universal.py b/Lib/site-packages/win32com/universal.py
new file mode 100644
index 0000000..d225661
--- /dev/null
+++ b/Lib/site-packages/win32com/universal.py
@@ -0,0 +1,196 @@
+# Code that packs and unpacks the Univgw structures.
+
+# See if we have a special directory for the binaries (for developers)
+import types
+import pythoncom
+from win32com.client import gencache
+
+com_error = pythoncom.com_error
+_univgw = pythoncom._univgw
+
+def RegisterInterfaces(typelibGUID, lcid, major, minor, interface_names = None):
+ ret = [] # return a list of (dispid, funcname for our policy's benefit
+ # First see if we have makepy support. If so, we can probably satisfy the request without loading the typelib.
+ try:
+ mod = gencache.GetModuleForTypelib(typelibGUID, lcid, major, minor)
+ except ImportError:
+ mod = None
+ if mod is None:
+ import win32com.client.build
+ # Load up the typelib and build (but don't cache) it now
+ tlb = pythoncom.LoadRegTypeLib(typelibGUID, major, minor, lcid)
+ typecomp_lib = tlb.GetTypeComp()
+ if interface_names is None:
+ interface_names = []
+ for i in range(tlb.GetTypeInfoCount()):
+ info = tlb.GetTypeInfo(i)
+ doc = tlb.GetDocumentation(i)
+ attr = info.GetTypeAttr()
+ if attr.typekind == pythoncom.TKIND_INTERFACE or \
+ (attr.typekind == pythoncom.TKIND_DISPATCH and attr.wTypeFlags & pythoncom.TYPEFLAG_FDUAL):
+ interface_names.append(doc[0])
+ for name in interface_names:
+ type_info, type_comp = typecomp_lib.BindType(name, )
+ # Not sure why we don't get an exception here - BindType's C
+ # impl looks correct..
+ if type_info is None:
+ raise ValueError("The interface '%s' can not be located" % (name,))
+ # If we got back a Dispatch interface, convert to the real interface.
+ attr = type_info.GetTypeAttr()
+ if attr.typekind == pythoncom.TKIND_DISPATCH:
+ refhtype = type_info.GetRefTypeOfImplType(-1)
+ type_info = type_info.GetRefTypeInfo(refhtype)
+ attr = type_info.GetTypeAttr()
+ item = win32com.client.build.VTableItem(type_info, attr, type_info.GetDocumentation(-1))
+ _doCreateVTable(item.clsid, item.python_name, item.bIsDispatch, item.vtableFuncs)
+ for info in item.vtableFuncs:
+ names, dispid, desc = info
+ invkind = desc[4]
+ ret.append((dispid, invkind, names[0]))
+ else:
+ # Cool - can used cached info.
+ if not interface_names:
+ interface_names = list(mod.VTablesToClassMap.values())
+ for name in interface_names:
+ try:
+ iid = mod.NamesToIIDMap[name]
+ except KeyError:
+ raise ValueError("Interface '%s' does not exist in this cached typelib" % (name,))
+# print "Processing interface", name
+ sub_mod = gencache.GetModuleForCLSID(iid)
+ is_dispatch = getattr(sub_mod, name + "_vtables_dispatch_", None)
+ method_defs = getattr(sub_mod, name + "_vtables_", None)
+ if is_dispatch is None or method_defs is None:
+ raise ValueError("Interface '%s' is IDispatch only" % (name,))
+
+ # And create the univgw defn
+ _doCreateVTable(iid, name, is_dispatch, method_defs)
+ for info in method_defs:
+ names, dispid, desc = info
+ invkind = desc[4]
+ ret.append((dispid, invkind, names[0]))
+ return ret
+
+def _doCreateVTable(iid, interface_name, is_dispatch, method_defs):
+ defn = Definition(iid, is_dispatch, method_defs)
+ vtbl = _univgw.CreateVTable(defn, is_dispatch)
+ _univgw.RegisterVTable(vtbl, iid, interface_name)
+
+def _CalcTypeSize(typeTuple):
+ t = typeTuple[0]
+ if t & (pythoncom.VT_BYREF | pythoncom.VT_ARRAY):
+ # Its a pointer.
+ cb = _univgw.SizeOfVT(pythoncom.VT_PTR)[1]
+ elif t == pythoncom.VT_RECORD:
+ # Just because a type library uses records doesn't mean the user
+ # is trying to. We need to better place to warn about this, but it
+ # isn't here.
+ #try:
+ # import warnings
+ # warnings.warn("warning: records are known to not work for vtable interfaces")
+ #except ImportError:
+ # print "warning: records are known to not work for vtable interfaces"
+ cb = _univgw.SizeOfVT(pythoncom.VT_PTR)[1]
+ #cb = typeInfo.GetTypeAttr().cbSizeInstance
+ else:
+ cb = _univgw.SizeOfVT(t)[1]
+ return cb
+
+class Arg:
+ def __init__(self, arg_info, name = None):
+ self.name = name
+ self.vt, self.inOut, self.default, self.clsid = arg_info
+ self.size = _CalcTypeSize(arg_info)
+ # Offset from the beginning of the arguments of the stack.
+ self.offset = 0
+
+class Method:
+ def __init__(self, method_info, isEventSink=0):
+ all_names, dispid, desc = method_info
+ name = all_names[0]
+ names = all_names[1:]
+ invkind = desc[4]
+ arg_defs = desc[2]
+ ret_def = desc[8]
+
+ self.dispid = dispid
+ self.invkind = invkind
+ # We dont use this ATM.
+# self.ret = Arg(ret_def)
+ if isEventSink and name[:2] != "On":
+ name = "On%s" % name
+ self.name = name
+ cbArgs = 0
+ self.args = []
+ for argDesc in arg_defs:
+ arg = Arg(argDesc)
+ arg.offset = cbArgs
+ cbArgs = cbArgs + arg.size
+ self.args.append(arg)
+ self.cbArgs = cbArgs
+ self._gw_in_args = self._GenerateInArgTuple()
+ self._gw_out_args = self._GenerateOutArgTuple()
+
+ def _GenerateInArgTuple(self):
+ # Given a method, generate the in argument tuple
+ l = []
+ for arg in self.args:
+ if arg.inOut & pythoncom.PARAMFLAG_FIN or \
+ arg.inOut == 0:
+ l.append((arg.vt, arg.offset, arg.size))
+ return tuple(l)
+
+ def _GenerateOutArgTuple(self):
+ # Given a method, generate the out argument tuple
+ l = []
+ for arg in self.args:
+ if arg.inOut & pythoncom.PARAMFLAG_FOUT or \
+ arg.inOut & pythoncom.PARAMFLAG_FRETVAL or \
+ arg.inOut == 0:
+ l.append((arg.vt, arg.offset, arg.size, arg.clsid))
+ return tuple(l)
+
+class Definition:
+ def __init__(self, iid, is_dispatch, method_defs):
+ self._iid = iid
+ self._methods = []
+ self._is_dispatch = is_dispatch
+ for info in method_defs:
+ entry = Method(info)
+ self._methods.append(entry)
+ def iid(self):
+ return self._iid
+ def vtbl_argsizes(self):
+ return [m.cbArgs for m in self._methods]
+ def vtbl_argcounts(self):
+ return [len(m.args) for m in self._methods]
+ def dispatch(self, ob, index, argPtr,
+ ReadFromInTuple=_univgw.ReadFromInTuple,
+ WriteFromOutTuple=_univgw.WriteFromOutTuple):
+ "Dispatch a call to an interface method."
+ meth = self._methods[index]
+ # Infer S_OK if they don't return anything bizarre.
+ hr = 0
+ args = ReadFromInTuple(meth._gw_in_args, argPtr)
+ # If ob is a dispatcher, ensure a policy
+ ob = getattr(ob, "policy", ob)
+ # Ensure the correct dispid is setup
+ ob._dispid_to_func_[meth.dispid] = meth.name
+ retVal = ob._InvokeEx_(meth.dispid, 0, meth.invkind, args, None, None)
+ # None is an allowed return value stating that
+ # the code doesn't want to touch any output arguments.
+ if type(retVal) == tuple: # Like pythoncom, we special case a tuple.
+ # However, if they want to return a specific HRESULT,
+ # then they have to return all of the out arguments
+ # AND the HRESULT.
+ if len(retVal) == len(meth._gw_out_args) + 1:
+ hr = retVal[0]
+ retVal = retVal[1:]
+ else:
+ raise TypeError("Expected %s return values, got: %s" % (len(meth._gw_out_args) + 1, len(retVal)))
+ else:
+ retVal = [retVal]
+ retVal.extend([None] * (len(meth._gw_out_args)-1))
+ retVal = tuple(retVal)
+ WriteFromOutTuple(retVal, meth._gw_out_args, argPtr)
+ return hr
diff --git a/Lib/site-packages/win32com/util.py b/Lib/site-packages/win32com/util.py
new file mode 100644
index 0000000..178a163
--- /dev/null
+++ b/Lib/site-packages/win32com/util.py
@@ -0,0 +1,31 @@
+"""General utility functions common to client and server.
+
+ This module contains a collection of general purpose utility functions.
+"""
+import pythoncom
+import win32api, win32con
+
+def IIDToInterfaceName(iid):
+ """Converts an IID to a string interface name.
+
+ Used primarily for debugging purposes, this allows a cryptic IID to
+ be converted to a useful string name. This will firstly look for interfaces
+ known (ie, registered) by pythoncom. If not known, it will look in the
+ registry for a registered interface.
+
+ iid -- An IID object.
+
+ Result -- Always a string - either an interface name, or ''
+ """
+ try:
+ return pythoncom.ServerInterfaces[iid]
+ except KeyError:
+ try:
+ try:
+ return win32api.RegQueryValue(win32con.HKEY_CLASSES_ROOT, "Interface\\%s" % iid)
+ except win32api.error:
+ pass
+ except ImportError:
+ pass
+ return str(iid)
+
diff --git a/Lib/site-packages/win32comext/adsi/__init__.py b/Lib/site-packages/win32comext/adsi/__init__.py
new file mode 100644
index 0000000..8636a3f
--- /dev/null
+++ b/Lib/site-packages/win32comext/adsi/__init__.py
@@ -0,0 +1,102 @@
+import win32com
+import win32com.client
+
+if type(__path__)==type(''):
+ # For freeze to work!
+ import sys
+ try:
+ import adsi
+ sys.modules['win32com.adsi.adsi'] = adsi
+ except ImportError:
+ pass
+else:
+ # See if we have a special directory for the binaries (for developers)
+ win32com.__PackageSupportBuildPath__(__path__)
+
+
+# Some helpers
+# We want to _look_ like the ADSI module, but provide some additional
+# helpers.
+
+# Of specific note - most of the interfaces supported by ADSI
+# derive from IDispatch - thus, you get the custome methods from the
+# interface, as well as via IDispatch.
+import pythoncom
+from adsi import *
+
+LCID = 0
+
+IDispatchType = pythoncom.TypeIIDs[pythoncom.IID_IDispatch]
+IADsContainerType = pythoncom.TypeIIDs[adsi.IID_IADsContainer]
+
+def _get_good_ret(ob,
+ # Named arguments used internally
+ resultCLSID = None):
+ assert resultCLSID is None, "Now have type info for ADSI objects - fix me!"
+ # See if the object supports IDispatch
+ if hasattr(ob, "Invoke"):
+ import win32com.client.dynamic
+ name = "Dispatch wrapper around %r" % ob
+ return win32com.client.dynamic.Dispatch(ob, name, ADSIDispatch)
+ return ob
+
+
+class ADSIEnumerator:
+ def __init__(self, ob):
+ # Query the object for the container interface.
+ self._cont_ = ob.QueryInterface(IID_IADsContainer)
+ self._oleobj_ = ADsBuildEnumerator(self._cont_) # a PyIADsEnumVARIANT
+ self.index = -1
+ def __getitem__(self, index):
+ return self.__GetIndex(index)
+ def __call__(self, index):
+ return self.__GetIndex(index)
+ def __GetIndex(self, index):
+ if type(index)!=type(0): raise TypeError("Only integer indexes are supported for enumerators")
+ if index != self.index + 1:
+ # Index requested out of sequence.
+ raise ValueError("You must index this object sequentially")
+ self.index = index
+ result = ADsEnumerateNext(self._oleobj_, 1)
+ if len(result):
+ return _get_good_ret(result[0])
+ # Failed - reset for next time around.
+ self.index = -1
+ self._oleobj_ = ADsBuildEnumerator(self._cont_) # a PyIADsEnumVARIANT
+ raise IndexError("list index out of range")
+
+class ADSIDispatch(win32com.client.CDispatch):
+ def _wrap_dispatch_(self, ob, userName = None, returnCLSID = None, UnicodeToString=None):
+ assert UnicodeToString is None, "this is deprectated and will be removed"
+ if not userName:
+ userName = "ADSI-object"
+ olerepr = win32com.client.dynamic.MakeOleRepr(ob, None, None)
+ return ADSIDispatch(ob, olerepr, userName)
+
+ def _NewEnum(self):
+ try:
+ return ADSIEnumerator(self)
+ except pythoncom.com_error:
+ # doesnt support it - let our base try!
+ return win32com.client.CDispatch._NewEnum(self)
+
+ def __getattr__(self, attr):
+ try:
+ return getattr(self._oleobj_, attr)
+ except AttributeError:
+ return win32com.client.CDispatch.__getattr__(self, attr)
+
+ def QueryInterface(self, iid):
+ ret = self._oleobj_.QueryInterface(iid)
+ return _get_good_ret(ret)
+
+# We override the global methods to do the right thing.
+_ADsGetObject = ADsGetObject # The one in the .pyd
+def ADsGetObject(path, iid = pythoncom.IID_IDispatch):
+ ret = _ADsGetObject(path, iid)
+ return _get_good_ret(ret)
+
+_ADsOpenObject = ADsOpenObject
+def ADsOpenObject(path, username, password, reserved = 0, iid = pythoncom.IID_IDispatch):
+ ret = _ADsOpenObject(path, username, password, reserved, iid)
+ return _get_good_ret(ret)
diff --git a/Lib/site-packages/win32comext/adsi/adsi.pyd b/Lib/site-packages/win32comext/adsi/adsi.pyd
new file mode 100644
index 0000000..6bbddf1
Binary files /dev/null and b/Lib/site-packages/win32comext/adsi/adsi.pyd differ
diff --git a/Lib/site-packages/win32comext/adsi/adsicon.py b/Lib/site-packages/win32comext/adsi/adsicon.py
new file mode 100644
index 0000000..17c6a89
--- /dev/null
+++ b/Lib/site-packages/win32comext/adsi/adsicon.py
@@ -0,0 +1,336 @@
+ADS_ATTR_CLEAR = ( 1 )
+ADS_ATTR_UPDATE = ( 2 )
+ADS_ATTR_APPEND = ( 3 )
+ADS_ATTR_DELETE = ( 4 )
+ADS_EXT_MINEXTDISPID = ( 1 )
+ADS_EXT_MAXEXTDISPID = ( 16777215 )
+ADS_EXT_INITCREDENTIALS = ( 1 )
+ADS_EXT_INITIALIZE_COMPLETE = ( 2 )
+
+ADS_SEARCHPREF_ASYNCHRONOUS = 0
+ADS_SEARCHPREF_DEREF_ALIASES = 1
+ADS_SEARCHPREF_SIZE_LIMIT = 2
+ADS_SEARCHPREF_TIME_LIMIT = 3
+ADS_SEARCHPREF_ATTRIBTYPES_ONLY = 4
+ADS_SEARCHPREF_SEARCH_SCOPE = 5
+ADS_SEARCHPREF_TIMEOUT = 6
+ADS_SEARCHPREF_PAGESIZE = 7
+ADS_SEARCHPREF_PAGED_TIME_LIMIT = 8
+ADS_SEARCHPREF_CHASE_REFERRALS = 9
+ADS_SEARCHPREF_SORT_ON = 10
+ADS_SEARCHPREF_CACHE_RESULTS = 11
+ADS_SEARCHPREF_DIRSYNC = 12
+ADS_SEARCHPREF_TOMBSTONE = 13
+
+ADS_SCOPE_BASE = 0
+ADS_SCOPE_ONELEVEL = 1
+ADS_SCOPE_SUBTREE = 2
+
+ADS_SECURE_AUTHENTICATION = 0x1
+ADS_USE_ENCRYPTION = 0x2
+ADS_USE_SSL = 0x2
+ADS_READONLY_SERVER = 0x4
+ADS_PROMPT_CREDENTIALS = 0x8
+ADS_NO_AUTHENTICATION = 0x10
+ADS_FAST_BIND = 0x20
+ADS_USE_SIGNING = 0x40
+ADS_USE_SEALING = 0x80
+ADS_USE_DELEGATION = 0x100
+ADS_SERVER_BIND = 0x200
+
+ADSTYPE_INVALID = 0
+ADSTYPE_DN_STRING = ADSTYPE_INVALID + 1
+ADSTYPE_CASE_EXACT_STRING = ADSTYPE_DN_STRING + 1
+ADSTYPE_CASE_IGNORE_STRING = ADSTYPE_CASE_EXACT_STRING + 1
+ADSTYPE_PRINTABLE_STRING = ADSTYPE_CASE_IGNORE_STRING + 1
+ADSTYPE_NUMERIC_STRING = ADSTYPE_PRINTABLE_STRING + 1
+ADSTYPE_BOOLEAN = ADSTYPE_NUMERIC_STRING + 1
+ADSTYPE_INTEGER = ADSTYPE_BOOLEAN + 1
+ADSTYPE_OCTET_STRING = ADSTYPE_INTEGER + 1
+ADSTYPE_UTC_TIME = ADSTYPE_OCTET_STRING + 1
+ADSTYPE_LARGE_INTEGER = ADSTYPE_UTC_TIME + 1
+ADSTYPE_PROV_SPECIFIC = ADSTYPE_LARGE_INTEGER + 1
+ADSTYPE_OBJECT_CLASS = ADSTYPE_PROV_SPECIFIC + 1
+ADSTYPE_CASEIGNORE_LIST = ADSTYPE_OBJECT_CLASS + 1
+ADSTYPE_OCTET_LIST = ADSTYPE_CASEIGNORE_LIST + 1
+ADSTYPE_PATH = ADSTYPE_OCTET_LIST + 1
+ADSTYPE_POSTALADDRESS = ADSTYPE_PATH + 1
+ADSTYPE_TIMESTAMP = ADSTYPE_POSTALADDRESS + 1
+ADSTYPE_BACKLINK = ADSTYPE_TIMESTAMP + 1
+ADSTYPE_TYPEDNAME = ADSTYPE_BACKLINK + 1
+ADSTYPE_HOLD = ADSTYPE_TYPEDNAME + 1
+ADSTYPE_NETADDRESS = ADSTYPE_HOLD + 1
+ADSTYPE_REPLICAPOINTER = ADSTYPE_NETADDRESS + 1
+ADSTYPE_FAXNUMBER = ADSTYPE_REPLICAPOINTER + 1
+ADSTYPE_EMAIL = ADSTYPE_FAXNUMBER + 1
+ADSTYPE_NT_SECURITY_DESCRIPTOR = ADSTYPE_EMAIL + 1
+ADSTYPE_UNKNOWN = ADSTYPE_NT_SECURITY_DESCRIPTOR + 1
+ADSTYPE_DN_WITH_BINARY = ADSTYPE_UNKNOWN + 1
+ADSTYPE_DN_WITH_STRING = ADSTYPE_DN_WITH_BINARY + 1
+
+ADS_PROPERTY_CLEAR = 1
+ADS_PROPERTY_UPDATE = 2
+ADS_PROPERTY_APPEND = 3
+ADS_PROPERTY_DELETE = 4
+ADS_SYSTEMFLAG_DISALLOW_DELETE = -2147483648
+ADS_SYSTEMFLAG_CONFIG_ALLOW_RENAME = 0x40000000
+ADS_SYSTEMFLAG_CONFIG_ALLOW_MOVE = 0x20000000
+ADS_SYSTEMFLAG_CONFIG_ALLOW_LIMITED_MOVE = 0x10000000
+ADS_SYSTEMFLAG_DOMAIN_DISALLOW_RENAME = -2147483648
+ADS_SYSTEMFLAG_DOMAIN_DISALLOW_MOVE = 0x4000000
+ADS_SYSTEMFLAG_CR_NTDS_NC = 0x1
+ADS_SYSTEMFLAG_CR_NTDS_DOMAIN = 0x2
+ADS_SYSTEMFLAG_ATTR_NOT_REPLICATED = 0x1
+ADS_SYSTEMFLAG_ATTR_IS_CONSTRUCTED = 0x4
+ADS_GROUP_TYPE_GLOBAL_GROUP = 0x2
+ADS_GROUP_TYPE_DOMAIN_LOCAL_GROUP = 0x4
+ADS_GROUP_TYPE_LOCAL_GROUP = 0x4
+ADS_GROUP_TYPE_UNIVERSAL_GROUP = 0x8
+ADS_GROUP_TYPE_SECURITY_ENABLED = -2147483648
+ADS_UF_SCRIPT = 0x1
+ADS_UF_ACCOUNTDISABLE = 0x2
+ADS_UF_HOMEDIR_REQUIRED = 0x8
+ADS_UF_LOCKOUT = 0x10
+ADS_UF_PASSWD_NOTREQD = 0x20
+ADS_UF_PASSWD_CANT_CHANGE = 0x40
+ADS_UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED = 0x80
+ADS_UF_TEMP_DUPLICATE_ACCOUNT = 0x100
+ADS_UF_NORMAL_ACCOUNT = 0x200
+ADS_UF_INTERDOMAIN_TRUST_ACCOUNT = 0x800
+ADS_UF_WORKSTATION_TRUST_ACCOUNT = 0x1000
+ADS_UF_SERVER_TRUST_ACCOUNT = 0x2000
+ADS_UF_DONT_EXPIRE_PASSWD = 0x10000
+ADS_UF_MNS_LOGON_ACCOUNT = 0x20000
+ADS_UF_SMARTCARD_REQUIRED = 0x40000
+ADS_UF_TRUSTED_FOR_DELEGATION = 0x80000
+ADS_UF_NOT_DELEGATED = 0x100000
+ADS_UF_USE_DES_KEY_ONLY = 0x200000
+ADS_UF_DONT_REQUIRE_PREAUTH = 0x400000
+ADS_UF_PASSWORD_EXPIRED = 0x800000
+ADS_UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION = 0x1000000
+ADS_RIGHT_DELETE = 0x10000
+ADS_RIGHT_READ_CONTROL = 0x20000
+ADS_RIGHT_WRITE_DAC = 0x40000
+ADS_RIGHT_WRITE_OWNER = 0x80000
+ADS_RIGHT_SYNCHRONIZE = 0x100000
+ADS_RIGHT_ACCESS_SYSTEM_SECURITY = 0x1000000
+ADS_RIGHT_GENERIC_READ = -2147483648
+ADS_RIGHT_GENERIC_WRITE = 0x40000000
+ADS_RIGHT_GENERIC_EXECUTE = 0x20000000
+ADS_RIGHT_GENERIC_ALL = 0x10000000
+ADS_RIGHT_DS_CREATE_CHILD = 0x1
+ADS_RIGHT_DS_DELETE_CHILD = 0x2
+ADS_RIGHT_ACTRL_DS_LIST = 0x4
+ADS_RIGHT_DS_SELF = 0x8
+ADS_RIGHT_DS_READ_PROP = 0x10
+ADS_RIGHT_DS_WRITE_PROP = 0x20
+ADS_RIGHT_DS_DELETE_TREE = 0x40
+ADS_RIGHT_DS_LIST_OBJECT = 0x80
+ADS_RIGHT_DS_CONTROL_ACCESS = 0x100
+ADS_ACETYPE_ACCESS_ALLOWED = 0
+ADS_ACETYPE_ACCESS_DENIED = 0x1
+ADS_ACETYPE_SYSTEM_AUDIT = 0x2
+ADS_ACETYPE_ACCESS_ALLOWED_OBJECT = 0x5
+ADS_ACETYPE_ACCESS_DENIED_OBJECT = 0x6
+ADS_ACETYPE_SYSTEM_AUDIT_OBJECT = 0x7
+ADS_ACETYPE_SYSTEM_ALARM_OBJECT = 0x8
+ADS_ACETYPE_ACCESS_ALLOWED_CALLBACK = 0x9
+ADS_ACETYPE_ACCESS_DENIED_CALLBACK = 0xa
+ADS_ACETYPE_ACCESS_ALLOWED_CALLBACK_OBJECT = 0xb
+ADS_ACETYPE_ACCESS_DENIED_CALLBACK_OBJECT = 0xc
+ADS_ACETYPE_SYSTEM_AUDIT_CALLBACK = 0xd
+ADS_ACETYPE_SYSTEM_ALARM_CALLBACK = 0xe
+ADS_ACETYPE_SYSTEM_AUDIT_CALLBACK_OBJECT = 0xf
+ADS_ACETYPE_SYSTEM_ALARM_CALLBACK_OBJECT = 0x10
+ADS_ACEFLAG_INHERIT_ACE = 0x2
+ADS_ACEFLAG_NO_PROPAGATE_INHERIT_ACE = 0x4
+ADS_ACEFLAG_INHERIT_ONLY_ACE = 0x8
+ADS_ACEFLAG_INHERITED_ACE = 0x10
+ADS_ACEFLAG_VALID_INHERIT_FLAGS = 0x1f
+ADS_ACEFLAG_SUCCESSFUL_ACCESS = 0x40
+ADS_ACEFLAG_FAILED_ACCESS = 0x80
+ADS_FLAG_OBJECT_TYPE_PRESENT = 0x1
+ADS_FLAG_INHERITED_OBJECT_TYPE_PRESENT = 0x2
+ADS_SD_CONTROL_SE_OWNER_DEFAULTED = 0x1
+ADS_SD_CONTROL_SE_GROUP_DEFAULTED = 0x2
+ADS_SD_CONTROL_SE_DACL_PRESENT = 0x4
+ADS_SD_CONTROL_SE_DACL_DEFAULTED = 0x8
+ADS_SD_CONTROL_SE_SACL_PRESENT = 0x10
+ADS_SD_CONTROL_SE_SACL_DEFAULTED = 0x20
+ADS_SD_CONTROL_SE_DACL_AUTO_INHERIT_REQ = 0x100
+ADS_SD_CONTROL_SE_SACL_AUTO_INHERIT_REQ = 0x200
+ADS_SD_CONTROL_SE_DACL_AUTO_INHERITED = 0x400
+ADS_SD_CONTROL_SE_SACL_AUTO_INHERITED = 0x800
+ADS_SD_CONTROL_SE_DACL_PROTECTED = 0x1000
+ADS_SD_CONTROL_SE_SACL_PROTECTED = 0x2000
+ADS_SD_CONTROL_SE_SELF_RELATIVE = 0x8000
+ADS_SD_REVISION_DS = 4
+ADS_NAME_TYPE_1779 = 1
+ADS_NAME_TYPE_CANONICAL = 2
+ADS_NAME_TYPE_NT4 = 3
+ADS_NAME_TYPE_DISPLAY = 4
+ADS_NAME_TYPE_DOMAIN_SIMPLE = 5
+ADS_NAME_TYPE_ENTERPRISE_SIMPLE = 6
+ADS_NAME_TYPE_GUID = 7
+ADS_NAME_TYPE_UNKNOWN = 8
+ADS_NAME_TYPE_USER_PRINCIPAL_NAME = 9
+ADS_NAME_TYPE_CANONICAL_EX = 10
+ADS_NAME_TYPE_SERVICE_PRINCIPAL_NAME = 11
+ADS_NAME_TYPE_SID_OR_SID_HISTORY_NAME = 12
+ADS_NAME_INITTYPE_DOMAIN = 1
+ADS_NAME_INITTYPE_SERVER = 2
+ADS_NAME_INITTYPE_GC = 3
+ADS_OPTION_SERVERNAME = 0
+ADS_OPTION_REFERRALS = ADS_OPTION_SERVERNAME + 1
+ADS_OPTION_PAGE_SIZE = ADS_OPTION_REFERRALS + 1
+ADS_OPTION_SECURITY_MASK = ADS_OPTION_PAGE_SIZE + 1
+ADS_OPTION_MUTUAL_AUTH_STATUS = ADS_OPTION_SECURITY_MASK + 1
+ADS_OPTION_QUOTA = ADS_OPTION_MUTUAL_AUTH_STATUS + 1
+ADS_OPTION_PASSWORD_PORTNUMBER = ADS_OPTION_QUOTA + 1
+ADS_OPTION_PASSWORD_METHOD = ADS_OPTION_PASSWORD_PORTNUMBER + 1
+ADS_SECURITY_INFO_OWNER = 0x1
+ADS_SECURITY_INFO_GROUP = 0x2
+ADS_SECURITY_INFO_DACL = 0x4
+ADS_SECURITY_INFO_SACL = 0x8
+ADS_SETTYPE_FULL = 1
+ADS_SETTYPE_PROVIDER = 2
+ADS_SETTYPE_SERVER = 3
+ADS_SETTYPE_DN = 4
+ADS_FORMAT_WINDOWS = 1
+ADS_FORMAT_WINDOWS_NO_SERVER = 2
+ADS_FORMAT_WINDOWS_DN = 3
+ADS_FORMAT_WINDOWS_PARENT = 4
+ADS_FORMAT_X500 = 5
+ADS_FORMAT_X500_NO_SERVER = 6
+ADS_FORMAT_X500_DN = 7
+ADS_FORMAT_X500_PARENT = 8
+ADS_FORMAT_SERVER = 9
+ADS_FORMAT_PROVIDER = 10
+ADS_FORMAT_LEAF = 11
+ADS_DISPLAY_FULL = 1
+ADS_DISPLAY_VALUE_ONLY = 2
+ADS_ESCAPEDMODE_DEFAULT = 1
+ADS_ESCAPEDMODE_ON = 2
+ADS_ESCAPEDMODE_OFF = 3
+ADS_ESCAPEDMODE_OFF_EX = 4
+ADS_PATH_FILE = 1
+ADS_PATH_FILESHARE = 2
+ADS_PATH_REGISTRY = 3
+ADS_SD_FORMAT_IID = 1
+ADS_SD_FORMAT_RAW = 2
+ADS_SD_FORMAT_HEXSTRING = 3
+
+
+# Generated by h2py from AdsErr.h
+def _HRESULT_TYPEDEF_(_sc): return _sc
+
+E_ADS_BAD_PATHNAME = _HRESULT_TYPEDEF_((-2147463168))
+E_ADS_INVALID_DOMAIN_OBJECT = _HRESULT_TYPEDEF_((-2147463167))
+E_ADS_INVALID_USER_OBJECT = _HRESULT_TYPEDEF_((-2147463166))
+E_ADS_INVALID_COMPUTER_OBJECT = _HRESULT_TYPEDEF_((-2147463165))
+E_ADS_UNKNOWN_OBJECT = _HRESULT_TYPEDEF_((-2147463164))
+E_ADS_PROPERTY_NOT_SET = _HRESULT_TYPEDEF_((-2147463163))
+E_ADS_PROPERTY_NOT_SUPPORTED = _HRESULT_TYPEDEF_((-2147463162))
+E_ADS_PROPERTY_INVALID = _HRESULT_TYPEDEF_((-2147463161))
+E_ADS_BAD_PARAMETER = _HRESULT_TYPEDEF_((-2147463160))
+E_ADS_OBJECT_UNBOUND = _HRESULT_TYPEDEF_((-2147463159))
+E_ADS_PROPERTY_NOT_MODIFIED = _HRESULT_TYPEDEF_((-2147463158))
+E_ADS_PROPERTY_MODIFIED = _HRESULT_TYPEDEF_((-2147463157))
+E_ADS_CANT_CONVERT_DATATYPE = _HRESULT_TYPEDEF_((-2147463156))
+E_ADS_PROPERTY_NOT_FOUND = _HRESULT_TYPEDEF_((-2147463155))
+E_ADS_OBJECT_EXISTS = _HRESULT_TYPEDEF_((-2147463154))
+E_ADS_SCHEMA_VIOLATION = _HRESULT_TYPEDEF_((-2147463153))
+E_ADS_COLUMN_NOT_SET = _HRESULT_TYPEDEF_((-2147463152))
+S_ADS_ERRORSOCCURRED = _HRESULT_TYPEDEF_(0x00005011)
+S_ADS_NOMORE_ROWS = _HRESULT_TYPEDEF_(0x00005012)
+S_ADS_NOMORE_COLUMNS = _HRESULT_TYPEDEF_(0x00005013)
+E_ADS_INVALID_FILTER = _HRESULT_TYPEDEF_((-2147463148))
+
+# ADS_DEREFENUM enum
+ADS_DEREF_NEVER = 0
+ADS_DEREF_SEARCHING = 1
+ADS_DEREF_FINDING = 2
+ADS_DEREF_ALWAYS = 3
+
+# ADS_PREFERENCES_ENUM
+ADSIPROP_ASYNCHRONOUS = 0
+ADSIPROP_DEREF_ALIASES = 0x1
+ADSIPROP_SIZE_LIMIT = 0x2
+ADSIPROP_TIME_LIMIT = 0x3
+ADSIPROP_ATTRIBTYPES_ONLY = 0x4
+ADSIPROP_SEARCH_SCOPE = 0x5
+ADSIPROP_TIMEOUT = 0x6
+ADSIPROP_PAGESIZE = 0x7
+ADSIPROP_PAGED_TIME_LIMIT = 0x8
+ADSIPROP_CHASE_REFERRALS = 0x9
+ADSIPROP_SORT_ON = 0xa
+ADSIPROP_CACHE_RESULTS = 0xb
+ADSIPROP_ADSIFLAG = 0xc
+
+# ADSI_DIALECT_ENUM
+ADSI_DIALECT_LDAP = 0
+ADSI_DIALECT_SQL = 0x1
+
+# ADS_CHASE_REFERRALS_ENUM
+ADS_CHASE_REFERRALS_NEVER = 0
+ADS_CHASE_REFERRALS_SUBORDINATE = 0x20
+ADS_CHASE_REFERRALS_EXTERNAL = 0x40
+ADS_CHASE_REFERRALS_ALWAYS = ADS_CHASE_REFERRALS_SUBORDINATE | ADS_CHASE_REFERRALS_EXTERNAL
+
+# Generated by h2py from ObjSel.h
+DSOP_SCOPE_TYPE_TARGET_COMPUTER = 0x00000001
+DSOP_SCOPE_TYPE_UPLEVEL_JOINED_DOMAIN = 0x00000002
+DSOP_SCOPE_TYPE_DOWNLEVEL_JOINED_DOMAIN = 0x00000004
+DSOP_SCOPE_TYPE_ENTERPRISE_DOMAIN = 0x00000008
+DSOP_SCOPE_TYPE_GLOBAL_CATALOG = 0x00000010
+DSOP_SCOPE_TYPE_EXTERNAL_UPLEVEL_DOMAIN = 0x00000020
+DSOP_SCOPE_TYPE_EXTERNAL_DOWNLEVEL_DOMAIN = 0x00000040
+DSOP_SCOPE_TYPE_WORKGROUP = 0x00000080
+DSOP_SCOPE_TYPE_USER_ENTERED_UPLEVEL_SCOPE = 0x00000100
+DSOP_SCOPE_TYPE_USER_ENTERED_DOWNLEVEL_SCOPE = 0x00000200
+DSOP_SCOPE_FLAG_STARTING_SCOPE = 0x00000001
+DSOP_SCOPE_FLAG_WANT_PROVIDER_WINNT = 0x00000002
+DSOP_SCOPE_FLAG_WANT_PROVIDER_LDAP = 0x00000004
+DSOP_SCOPE_FLAG_WANT_PROVIDER_GC = 0x00000008
+DSOP_SCOPE_FLAG_WANT_SID_PATH = 0x00000010
+DSOP_SCOPE_FLAG_WANT_DOWNLEVEL_BUILTIN_PATH = 0x00000020
+DSOP_SCOPE_FLAG_DEFAULT_FILTER_USERS = 0x00000040
+DSOP_SCOPE_FLAG_DEFAULT_FILTER_GROUPS = 0x00000080
+DSOP_SCOPE_FLAG_DEFAULT_FILTER_COMPUTERS = 0x00000100
+DSOP_SCOPE_FLAG_DEFAULT_FILTER_CONTACTS = 0x00000200
+DSOP_FILTER_INCLUDE_ADVANCED_VIEW = 0x00000001
+DSOP_FILTER_USERS = 0x00000002
+DSOP_FILTER_BUILTIN_GROUPS = 0x00000004
+DSOP_FILTER_WELL_KNOWN_PRINCIPALS = 0x00000008
+DSOP_FILTER_UNIVERSAL_GROUPS_DL = 0x00000010
+DSOP_FILTER_UNIVERSAL_GROUPS_SE = 0x00000020
+DSOP_FILTER_GLOBAL_GROUPS_DL = 0x00000040
+DSOP_FILTER_GLOBAL_GROUPS_SE = 0x00000080
+DSOP_FILTER_DOMAIN_LOCAL_GROUPS_DL = 0x00000100
+DSOP_FILTER_DOMAIN_LOCAL_GROUPS_SE = 0x00000200
+DSOP_FILTER_CONTACTS = 0x00000400
+DSOP_FILTER_COMPUTERS = 0x00000800
+DSOP_DOWNLEVEL_FILTER_USERS = (-2147483647)
+DSOP_DOWNLEVEL_FILTER_LOCAL_GROUPS = (-2147483646)
+DSOP_DOWNLEVEL_FILTER_GLOBAL_GROUPS = (-2147483644)
+DSOP_DOWNLEVEL_FILTER_COMPUTERS = (-2147483640)
+DSOP_DOWNLEVEL_FILTER_WORLD = (-2147483632)
+DSOP_DOWNLEVEL_FILTER_AUTHENTICATED_USER = (-2147483616)
+DSOP_DOWNLEVEL_FILTER_ANONYMOUS = (-2147483584)
+DSOP_DOWNLEVEL_FILTER_BATCH = (-2147483520)
+DSOP_DOWNLEVEL_FILTER_CREATOR_OWNER = (-2147483392)
+DSOP_DOWNLEVEL_FILTER_CREATOR_GROUP = (-2147483136)
+DSOP_DOWNLEVEL_FILTER_DIALUP = (-2147482624)
+DSOP_DOWNLEVEL_FILTER_INTERACTIVE = (-2147481600)
+DSOP_DOWNLEVEL_FILTER_NETWORK = (-2147479552)
+DSOP_DOWNLEVEL_FILTER_SERVICE = (-2147475456)
+DSOP_DOWNLEVEL_FILTER_SYSTEM = (-2147467264)
+DSOP_DOWNLEVEL_FILTER_EXCLUDE_BUILTIN_GROUPS = (-2147450880)
+DSOP_DOWNLEVEL_FILTER_TERMINAL_SERVER = (-2147418112)
+DSOP_DOWNLEVEL_FILTER_ALL_WELLKNOWN_SIDS = (-2147352576)
+DSOP_DOWNLEVEL_FILTER_LOCAL_SERVICE = (-2147221504)
+DSOP_DOWNLEVEL_FILTER_NETWORK_SERVICE = (-2146959360)
+DSOP_DOWNLEVEL_FILTER_REMOTE_LOGON = (-2146435072)
+DSOP_FLAG_MULTISELECT = 0x00000001
+DSOP_FLAG_SKIP_TARGET_COMPUTER_DC_CHECK = 0x00000002
+CFSTR_DSOP_DS_SELECTION_LIST = "CFSTR_DSOP_DS_SELECTION_LIST"
diff --git a/Lib/site-packages/win32comext/adsi/demos/objectPicker.py b/Lib/site-packages/win32comext/adsi/demos/objectPicker.py
new file mode 100644
index 0000000..890bd68
--- /dev/null
+++ b/Lib/site-packages/win32comext/adsi/demos/objectPicker.py
@@ -0,0 +1,58 @@
+# A demo for the IDsObjectPicker interface.
+import win32clipboard
+import pythoncom
+from win32com.adsi import adsi
+from win32com.adsi.adsicon import *
+
+cf_objectpicker = win32clipboard.RegisterClipboardFormat(CFSTR_DSOP_DS_SELECTION_LIST)
+
+def main():
+ hwnd = 0
+
+ # Create an instance of the object picker.
+ picker = pythoncom.CoCreateInstance(adsi.CLSID_DsObjectPicker,
+ None,
+ pythoncom.CLSCTX_INPROC_SERVER,
+ adsi.IID_IDsObjectPicker)
+
+ # Create our scope init info.
+ siis = adsi.DSOP_SCOPE_INIT_INFOs(1)
+ sii = siis[0]
+
+ # Combine multiple scope types in a single array entry.
+
+ sii.type = DSOP_SCOPE_TYPE_UPLEVEL_JOINED_DOMAIN | \
+ DSOP_SCOPE_TYPE_DOWNLEVEL_JOINED_DOMAIN
+
+ # Set uplevel and downlevel filters to include only computer objects.
+ # Uplevel filters apply to both mixed and native modes.
+ # Notice that the uplevel and downlevel flags are different.
+
+ sii.filterFlags.uplevel.bothModes = DSOP_FILTER_COMPUTERS
+ sii.filterFlags.downlevel = DSOP_DOWNLEVEL_FILTER_COMPUTERS
+
+ # Initialize the interface.
+ picker.Initialize(
+ None, # Target is the local computer.
+ siis, # scope infos
+ DSOP_FLAG_MULTISELECT, # options
+ ('objectGUID','displayName') ) # attributes to fetch
+
+ do = picker.InvokeDialog(hwnd)
+ # Extract the data from the IDataObject.
+ format_etc = (cf_objectpicker, None,
+ pythoncom.DVASPECT_CONTENT, -1,
+ pythoncom.TYMED_HGLOBAL)
+ medium = do.GetData(format_etc)
+ data = adsi.StringAsDS_SELECTION_LIST(medium.data)
+ for item in data:
+ name, klass, adspath, upn, attrs, flags = item
+ print("Item", name)
+ print(" Class:", klass)
+ print(" AdsPath:", adspath)
+ print(" UPN:", upn)
+ print(" Attrs:", attrs)
+ print(" Flags:", flags)
+
+if __name__=='__main__':
+ main()
diff --git a/Lib/site-packages/win32comext/adsi/demos/scp.py b/Lib/site-packages/win32comext/adsi/demos/scp.py
new file mode 100644
index 0000000..58615f9
--- /dev/null
+++ b/Lib/site-packages/win32comext/adsi/demos/scp.py
@@ -0,0 +1,492 @@
+"""A re-implementation of the MS DirectoryService samples related to services.
+
+* Adds and removes an ActiveDirectory "Service Connection Point",
+ including managing the security on the object.
+* Creates and registers Service Principal Names.
+* Changes the username for a domain user.
+
+Some of these functions are likely to become move to a module - but there
+is also a little command-line-interface to try these functions out.
+
+For example:
+
+scp.py --account-name=domain\\user --service-class=PythonScpTest \\
+ --keyword=foo --keyword=bar --binding-string=bind_info \\
+ ScpCreate SpnCreate SpnRegister
+
+would:
+* Attempt to delete a Service Connection Point for the service class
+ 'PythonScpTest'
+* Attempt to create a Service Connection Point for that class, with 2
+ keywords and a binding string of 'bind_info'
+* Create a Service Principal Name for the service and register it
+
+to undo those changes, you could execute:
+
+scp.py --account-name=domain\\user --service-class=PythonScpTest \\
+ SpnCreate SpnUnregister ScpDelete
+
+which will:
+* Create a SPN
+* Unregister that SPN from the Active Directory.
+* Delete the Service Connection Point
+
+Executing with --test will create and remove one of everything.
+"""
+
+from win32com.adsi.adsicon import *
+from win32com.adsi import adsi
+import win32api, win32con, winerror
+from win32com.client import Dispatch
+import ntsecuritycon as dscon
+import win32security
+import optparse, textwrap
+import traceback
+
+verbose = 1
+g_createdSCP = None
+g_createdSPNs = []
+g_createdSPNLast = None
+
+import logging
+
+logger = logging # use logging module global methods for now.
+
+# still a bit confused about log(n, ...) vs logger.info/debug()
+
+# Returns distinguished name of SCP.
+def ScpCreate(
+ service_binding_info,
+ service_class_name, # Service class string to store in SCP.
+ account_name = None, # Logon account that needs access to SCP.
+ container_name = None,
+ keywords = None,
+ object_class = "serviceConnectionPoint",
+ dns_name_type = "A",
+ dn = None,
+ dns_name = None,
+ ):
+ container_name = container_name or service_class_name
+ if not dns_name:
+ # Get the DNS name of the local computer
+ dns_name = win32api.GetComputerNameEx(win32con.ComputerNameDnsFullyQualified)
+ # Get the distinguished name of the computer object for the local computer
+ if dn is None:
+ dn = win32api.GetComputerObjectName(win32con.NameFullyQualifiedDN)
+
+ # Compose the ADSpath and bind to the computer object for the local computer
+ comp = adsi.ADsGetObject("LDAP://" + dn, adsi.IID_IDirectoryObject)
+
+ # Publish the SCP as a child of the computer object
+ keywords = keywords or []
+ # Fill in the attribute values to be stored in the SCP.
+ attrs = [
+ ("cn", ADS_ATTR_UPDATE, ADSTYPE_CASE_IGNORE_STRING, (container_name,)),
+ ("objectClass", ADS_ATTR_UPDATE, ADSTYPE_CASE_IGNORE_STRING, (object_class,)),
+ ("keywords", ADS_ATTR_UPDATE, ADSTYPE_CASE_IGNORE_STRING, keywords),
+ ("serviceDnsName", ADS_ATTR_UPDATE, ADSTYPE_CASE_IGNORE_STRING, (dns_name,)),
+ ("serviceDnsNameType", ADS_ATTR_UPDATE, ADSTYPE_CASE_IGNORE_STRING, (dns_name_type,)),
+ ("serviceClassName", ADS_ATTR_UPDATE, ADSTYPE_CASE_IGNORE_STRING, (service_class_name,)),
+ ("serviceBindingInformation", ADS_ATTR_UPDATE, ADSTYPE_CASE_IGNORE_STRING, (service_binding_info,)),
+ ]
+ new = comp.CreateDSObject("cn=" + container_name, attrs)
+ logger.info("New connection point is at %s", container_name)
+ # Wrap in a usable IDispatch object.
+ new = Dispatch(new)
+ # And allow access to the SCP for the specified account name
+ AllowAccessToScpProperties(account_name, new)
+ return new
+
+def ScpDelete(container_name, dn = None):
+ if dn is None:
+ dn = win32api.GetComputerObjectName(win32con.NameFullyQualifiedDN)
+ logger.debug("Removing connection point '%s' from %s", container_name, dn)
+
+ # Compose the ADSpath and bind to the computer object for the local computer
+ comp = adsi.ADsGetObject("LDAP://" + dn, adsi.IID_IDirectoryObject)
+ comp.DeleteDSObject("cn=" + container_name)
+ logger.info("Deleted service connection point '%s'", container_name)
+
+# This function is described in detail in the MSDN article titled
+# "Enabling Service Account to Access SCP Properties"
+# From that article:
+# The following sample code sets a pair of ACEs on a service connection point
+# (SCP) object. The ACEs grant read/write access to the user or computer account
+# under which the service instance will be running. Your service installation
+# program calls this code to ensure that the service will be allowed to update
+# its properties at run time. If you don't set ACEs like these, your service
+# will get access-denied errors if it tries to modify the SCP's properties.
+#
+# The code uses the IADsSecurityDescriptor, IADsAccessControlList, and
+# IADsAccessControlEntry interfaces to do the following:
+# * Get the SCP object's security descriptor.
+# * Set ACEs in the DACL of the security descriptor.
+# * Set the security descriptor back on the SCP object.
+
+def AllowAccessToScpProperties(
+ accountSAM, #Service account to allow access.
+ scpObject, # The IADs SCP object.
+ schemaIDGUIDs = # Attributes to allow write-access to.
+ ("{28630eb8-41d5-11d1-a9c1-0000f80367c1}", # serviceDNSName
+ "{b7b1311c-b82e-11d0-afee-0000f80367c1}", # serviceBindingInformation
+ )
+ ):
+
+ # If no service account is specified, service runs under LocalSystem.
+ # So allow access to the computer account of the service's host.
+ if accountSAM:
+ trustee = accountSAM
+ else:
+ # Get the SAM account name of the computer object for the server.
+ trustee = win32api.GetComputerObjectName(win32con.NameSamCompatible)
+
+ # Get the nTSecurityDescriptor attribute
+ attribute = "nTSecurityDescriptor"
+ sd = getattr(scpObject, attribute)
+ acl = sd.DiscretionaryAcl
+
+ for sguid in schemaIDGUIDs:
+ ace = Dispatch(adsi.CLSID_AccessControlEntry)
+
+ # Set the properties of the ACE.
+ # Allow read and write access to the property.
+ ace.AccessMask = ADS_RIGHT_DS_READ_PROP | ADS_RIGHT_DS_WRITE_PROP
+
+ # Set the trustee, which is either the service account or the
+ # host computer account.
+ ace.Trustee = trustee
+
+ # Set the ACE type.
+ ace.AceType = ADS_ACETYPE_ACCESS_ALLOWED_OBJECT
+
+ # Set AceFlags to zero because ACE is not inheritable.
+ ace.AceFlags = 0
+
+ # Set Flags to indicate an ACE that protects a specified object.
+ ace.Flags = ADS_FLAG_OBJECT_TYPE_PRESENT
+
+ # Set ObjectType to the schemaIDGUID of the attribute.
+ ace.ObjectType = sguid
+
+ # Add the ACEs to the DACL.
+ acl.AddAce(ace)
+
+ # Write the modified DACL back to the security descriptor.
+ sd.DiscretionaryAcl = acl
+ # Write the ntSecurityDescriptor property to the property cache.
+ setattr(scpObject, attribute, sd)
+ # SetInfo updates the SCP object in the directory.
+ scpObject.SetInfo()
+ logger.info("Set security on object for account '%s'" % (trustee,))
+
+# Service Principal Names functions from the same sample.
+# The example calls the DsWriteAccountSpn function, which stores the SPNs in
+# Microsoft Active Directory under the servicePrincipalName attribute of the
+# account object specified by the serviceAcctDN parameter. The account object
+# corresponds to the logon account specified in the CreateService call for this
+# service instance. If the logon account is a domain user account,
+# serviceAcctDN must be the distinguished name of the account object in
+# Active Directory for that user account. If the service's logon account is the
+# LocalSystem account, serviceAcctDN must be the distinguished name of the
+# computer account object for the host computer on which the service is
+# installed. win32api.TranslateNames and win32security.DsCrackNames can
+# be used to convert a domain\account format name to a distinguished name.
+def SpnRegister(
+ serviceAcctDN, # DN of the service's logon account
+ spns, # List of SPNs to register
+ operation, # Add, replace, or delete SPNs
+ ):
+ assert type(spns) not in [str, str] and hasattr(spns, "__iter__"), \
+ "spns must be a sequence of strings (got %r)" % spns
+ # Bind to a domain controller.
+ # Get the domain for the current user.
+ samName = win32api.GetUserNameEx(win32api.NameSamCompatible)
+ samName = samName.split('\\', 1)[0]
+
+ if not serviceAcctDN:
+ # Get the SAM account name of the computer object for the server.
+ serviceAcctDN = win32api.GetComputerObjectName(win32con.NameFullyQualifiedDN)
+ logger.debug("SpnRegister using DN '%s'", serviceAcctDN)
+
+ # Get the name of a domain controller in that domain.
+ info = win32security.DsGetDcName(
+ domainName=samName,
+ flags=dscon.DS_IS_FLAT_NAME |
+ dscon.DS_RETURN_DNS_NAME |
+ dscon.DS_DIRECTORY_SERVICE_REQUIRED)
+ # Bind to the domain controller.
+ handle = win32security.DsBind( info['DomainControllerName'] )
+
+ # Write the SPNs to the service account or computer account.
+ logger.debug("DsWriteAccountSpn with spns %s")
+ win32security.DsWriteAccountSpn(
+ handle, # handle to the directory
+ operation, # Add or remove SPN from account's existing SPNs
+ serviceAcctDN, # DN of service account or computer account
+ spns) # names
+
+ # Unbind the DS in any case (but Python would do it anyway)
+ handle.Close()
+
+def UserChangePassword(username_dn, new_password):
+ # set the password on the account.
+ # Use the distinguished name to bind to the account object.
+ accountPath = "LDAP://" + username_dn
+ user = adsi.ADsGetObject(accountPath, adsi.IID_IADsUser)
+
+ # Set the password on the account.
+ user.SetPassword(new_password)
+
+# functions related to the command-line interface
+def log(level, msg, *args):
+ if verbose >= level:
+ print(msg % args)
+
+class _NoDefault: pass
+
+def _get_option(po, opt_name, default = _NoDefault):
+ parser, options = po
+ ret = getattr(options, opt_name, default)
+ if not ret and default is _NoDefault:
+ parser.error("The '%s' option must be specified for this operation" % opt_name)
+ if not ret:
+ ret = default
+ return ret
+
+def _option_error(po, why):
+ parser = po[0]
+ parser.error(why)
+
+def do_ScpCreate(po):
+ """Create a Service Connection Point"""
+ global g_createdSCP
+ scp = ScpCreate(_get_option(po, "binding_string"),
+ _get_option(po, "service_class"),
+ _get_option(po, "account_name_sam", None),
+ keywords=_get_option(po, "keywords", None))
+ g_createdSCP = scp
+ return scp.distinguishedName
+
+def do_ScpDelete(po):
+ """Delete a Service Connection Point"""
+ sc = _get_option(po, "service_class")
+ try:
+ ScpDelete(sc)
+ except adsi.error as details:
+ if details[0] != winerror.ERROR_DS_OBJ_NOT_FOUND:
+ raise
+ log(2, "ScpDelete ignoring ERROR_DS_OBJ_NOT_FOUND for service-class '%s'",
+ sc)
+ return sc
+
+def do_SpnCreate(po):
+ """Create a Service Principal Name"""
+ # The 'service name' is the dn of our scp.
+ if g_createdSCP is None:
+ # Could accept an arg to avoid this?
+ _option_error(po, "ScpCreate must have been specified before SpnCreate")
+ # Create a Service Principal Name"
+ spns = win32security.DsGetSpn(dscon.DS_SPN_SERVICE,
+ _get_option(po, "service_class"),
+ g_createdSCP.distinguishedName,
+ _get_option(po, "port", 0),
+ None, None)
+ spn = spns[0]
+ log(2, "Created SPN: %s", spn)
+ global g_createdSPNLast
+ g_createdSPNLast = spn
+ g_createdSPNs.append(spn)
+ return spn
+
+def do_SpnRegister(po):
+ """Register a previously created Service Principal Name"""
+ if not g_createdSPNLast:
+ _option_error(po, "SpnCreate must appear before SpnRegister")
+
+ SpnRegister(_get_option(po, "account_name_dn", None),
+ (g_createdSPNLast,),
+ dscon.DS_SPN_ADD_SPN_OP)
+ return g_createdSPNLast
+
+def do_SpnUnregister(po):
+ """Unregister a previously created Service Principal Name"""
+ if not g_createdSPNLast:
+ _option_error(po, "SpnCreate must appear before SpnUnregister")
+ SpnRegister(_get_option(po, "account_name_dn", None),
+ (g_createdSPNLast,),
+ dscon.DS_SPN_DELETE_SPN_OP)
+ return g_createdSPNLast
+
+def do_UserChangePassword(po):
+ """Change the password for a specified user"""
+ UserChangePassword(_get_option(po, "account_name_dn"),
+ _get_option(po, "password"))
+ return "Password changed OK"
+
+handlers = (
+ ('ScpCreate', do_ScpCreate),
+ ('ScpDelete', do_ScpDelete),
+ ('SpnCreate', do_SpnCreate),
+ ('SpnRegister', do_SpnRegister),
+ ('SpnUnregister', do_SpnUnregister),
+ ('UserChangePassword', do_UserChangePassword),
+ )
+
+class HelpFormatter(optparse.IndentedHelpFormatter):
+ def format_description(self, description):
+ return description
+
+def main():
+ global verbose
+ _handlers_dict = {}
+
+ arg_descs = []
+ for arg, func in handlers:
+ this_desc = "\n".join(textwrap.wrap(func.__doc__,
+ subsequent_indent = " " * 8))
+ arg_descs.append(" %s: %s" % (arg, this_desc))
+ _handlers_dict[arg.lower()] = func
+
+ description = __doc__ + "\ncommands:\n" + "\n".join(arg_descs) + "\n"
+
+ parser = optparse.OptionParser(usage = "%prog [options] command ...",
+ description=description,
+ formatter=HelpFormatter())
+
+ parser.add_option("-v", action="count",
+ dest="verbose", default=1,
+ help="increase the verbosity of status messages")
+
+ parser.add_option("-q", "--quiet", action="store_true",
+ help="Don't print any status messages")
+
+ parser.add_option("-t", "--test", action="store_true",
+ help="Execute a mini-test suite, providing defaults for most options and args"),
+
+ parser.add_option("", "--show-tracebacks", action="store_true",
+ help="Show the tracebacks for any exceptions")
+
+ parser.add_option("", "--service-class",
+ help="The service class name to use")
+
+ parser.add_option("", "--port", default=0,
+ help="The port number to associate with the SPN")
+
+ parser.add_option("", "--binding-string",
+ help="The binding string to use for SCP creation")
+
+ parser.add_option("", "--account-name",
+ help="The account name to use (default is LocalSystem)")
+
+ parser.add_option("", "--password",
+ help="The password to set.")
+
+ parser.add_option("", "--keyword", action="append", dest="keywords",
+ help="""A keyword to add to the SCP. May be specified
+ multiple times""")
+
+ parser.add_option("", "--log-level",
+ help="""The log-level to use - may be a number or a logging
+ module constant""", default=str(logging.WARNING))
+
+ options, args = parser.parse_args()
+ po = (parser, options)
+ # fixup misc
+ try:
+ options.port = int(options.port)
+ except (TypeError, ValueError):
+ parser.error("--port must be numeric")
+ # fixup log-level
+ try:
+ log_level = int(options.log_level)
+ except (TypeError, ValueError):
+ try:
+ log_level = int(getattr(logging, options.log_level.upper()))
+ except (ValueError, TypeError, AttributeError):
+ parser.error("Invalid --log-level value")
+ try:
+ sl = logger.setLevel
+ # logger is a real logger
+ except AttributeError:
+ # logger is logging module
+ sl = logging.getLogger().setLevel
+ sl(log_level)
+ # Check -q/-v
+ if options.quiet and options.verbose:
+ parser.error("Can't specify --quiet and --verbose")
+ if options.quiet:
+ options.verbose -= 1
+ verbose = options.verbose
+ # --test
+ if options.test:
+ if args:
+ parser.error("Can't specify args with --test")
+
+ args = "ScpDelete ScpCreate SpnCreate SpnRegister SpnUnregister ScpDelete"
+ log(1, "--test - pretending args are:\n %s", args)
+ args = args.split()
+ if not options.service_class:
+ options.service_class = "PythonScpTest"
+ log(2, "--test: --service-class=%s", options.service_class)
+ if not options.keywords:
+ options.keywords = "Python Powered".split()
+ log(2, "--test: --keyword=%s", options.keywords)
+ if not options.binding_string:
+ options.binding_string = "test binding string"
+ log(2, "--test: --binding-string=%s", options.binding_string)
+
+ # check args
+ if not args:
+ parser.error("No command specified (use --help for valid commands)")
+ for arg in args:
+ if arg.lower() not in _handlers_dict:
+ parser.error("Invalid command '%s' (use --help for valid commands)" % arg)
+
+ # Patch up account-name.
+ if options.account_name:
+ log(2, "Translating account name '%s'", options.account_name)
+ options.account_name_sam = win32security.TranslateName(options.account_name,
+ win32api.NameUnknown,
+ win32api.NameSamCompatible)
+ log(2, "NameSamCompatible is '%s'",options.account_name_sam)
+ options.account_name_dn = win32security.TranslateName(options.account_name,
+ win32api.NameUnknown,
+ win32api.NameFullyQualifiedDN)
+ log(2, "NameFullyQualifiedDNis '%s'",options.account_name_dn)
+
+ # do it.
+ for arg in args:
+ handler = _handlers_dict[arg.lower()] # already been validated
+ if handler is None:
+ parser.error("Invalid command '%s'" % arg)
+ err_msg = None
+ try:
+ try:
+ log(2, "Executing '%s'...", arg)
+ result = handler(po)
+ log(1, "%s: %s", arg, result)
+ except:
+ if options.show_tracebacks:
+ print("--show-tracebacks specified - dumping exception")
+ traceback.print_exc()
+ raise
+ except adsi.error as xxx_todo_changeme:
+ (hr, desc, exc, argerr) = xxx_todo_changeme.args
+ if exc:
+ extra_desc = exc[2]
+ else:
+ extra_desc = ""
+ err_msg = desc
+ if extra_desc:
+ err_msg += "\n\t" + extra_desc
+ except win32api.error as xxx_todo_changeme1:
+ (hr, func, msg) = xxx_todo_changeme1.args
+ err_msg = msg
+ if err_msg:
+ log(1, "Command '%s' failed: %s", arg, err_msg)
+
+if __name__=='__main__':
+ try:
+ main()
+ except KeyboardInterrupt:
+ print("*** Interrupted")
diff --git a/Lib/site-packages/win32comext/adsi/demos/search.py b/Lib/site-packages/win32comext/adsi/demos/search.py
new file mode 100644
index 0000000..c8f4516
--- /dev/null
+++ b/Lib/site-packages/win32comext/adsi/demos/search.py
@@ -0,0 +1,132 @@
+from win32com.adsi import adsi
+from win32com.adsi.adsicon import *
+from win32com.adsi import adsicon
+import pythoncom, pywintypes, win32security
+
+options = None # set to optparse options object
+
+ADsTypeNameMap = {}
+
+def getADsTypeName(type_val):
+ # convert integer type to the 'typename' as known in the headerfiles.
+ if not ADsTypeNameMap:
+ for n, v in adsicon.__dict__.items():
+ if n.startswith("ADSTYPE_"):
+ ADsTypeNameMap[v] = n
+ return ADsTypeNameMap.get(type_val, hex(type_val))
+
+def _guid_from_buffer(b):
+ return pywintypes.IID(b, True)
+
+def _sid_from_buffer(b):
+ return str(pywintypes.SID(b))
+
+_null_converter = lambda x: x
+
+converters = {
+ 'objectGUID' : _guid_from_buffer,
+ 'objectSid' : _sid_from_buffer,
+ 'instanceType' : getADsTypeName,
+}
+
+def log(level, msg, *args):
+ if options.verbose >= level:
+ print("log:", msg % args)
+
+def getGC():
+ cont = adsi.ADsOpenObject("GC:", options.user, options.password, 0, adsi.IID_IADsContainer)
+ enum = adsi.ADsBuildEnumerator(cont)
+ # Only 1 child of the global catalog.
+ for e in enum:
+ gc = e.QueryInterface(adsi.IID_IDirectorySearch)
+ return gc
+ return None
+
+def print_attribute(col_data):
+ prop_name, prop_type, values = col_data
+ if values is not None:
+ log(2, "property '%s' has type '%s'", prop_name, getADsTypeName(prop_type))
+ value = [converters.get(prop_name, _null_converter)(v[0]) for v in values]
+ if len(value) == 1:
+ value = value[0]
+ print(" %s=%r" % (prop_name, value))
+ else:
+ print(" %s is None" % (prop_name,))
+
+def search():
+ gc = getGC()
+ if gc is None:
+ log(0, "Can't find the global catalog")
+ return
+
+ prefs = [(ADS_SEARCHPREF_SEARCH_SCOPE, (ADS_SCOPE_SUBTREE,))]
+ hr, statuses = gc.SetSearchPreference(prefs)
+ log(3, "SetSearchPreference returned %d/%r", hr, statuses)
+
+ if options.attributes:
+ attributes = options.attributes.split(",")
+ else:
+ attributes = None
+
+ h = gc.ExecuteSearch(options.filter, attributes)
+ hr = gc.GetNextRow(h)
+ while hr != S_ADS_NOMORE_ROWS:
+ print("-- new row --")
+ if attributes is None:
+ # Loop over all columns returned
+ while 1:
+ col_name = gc.GetNextColumnName(h)
+ if col_name is None:
+ break
+ data = gc.GetColumn(h, col_name)
+ print_attribute(data)
+ else:
+ # loop over attributes specified.
+ for a in attributes:
+ try:
+ data = gc.GetColumn(h, a)
+ print_attribute(data)
+ except adsi.error as details:
+ if details[0] != E_ADS_COLUMN_NOT_SET:
+ raise
+ print_attribute( (a, None, None) )
+ hr = gc.GetNextRow(h)
+ gc.CloseSearchHandle(h)
+
+def main():
+ global options
+ from optparse import OptionParser
+
+ parser = OptionParser()
+ parser.add_option("-f", "--file", dest="filename",
+ help="write report to FILE", metavar="FILE")
+ parser.add_option("-v", "--verbose",
+ action="count", default=1,
+ help="increase verbosity of output")
+ parser.add_option("-q", "--quiet",
+ action="store_true",
+ help="suppress output messages")
+
+ parser.add_option("-U", "--user",
+ help="specify the username used to connect")
+ parser.add_option("-P", "--password",
+ help="specify the password used to connect")
+ parser.add_option("", "--filter",
+ default = "(&(objectCategory=person)(objectClass=User))",
+ help="specify the search filter")
+ parser.add_option("", "--attributes",
+ help="comma sep'd list of attribute names to print")
+
+ options, args = parser.parse_args()
+ if options.quiet:
+ if options.verbose != 1:
+ parser.error("Can not use '--verbose' and '--quiet'")
+ options.verbose = 0
+
+ if args:
+ parser.error("You need not specify args")
+
+ search()
+
+if __name__=='__main__':
+ main()
diff --git a/Lib/site-packages/win32comext/adsi/demos/test.py b/Lib/site-packages/win32comext/adsi/demos/test.py
new file mode 100644
index 0000000..4ce7b00
--- /dev/null
+++ b/Lib/site-packages/win32comext/adsi/demos/test.py
@@ -0,0 +1,247 @@
+import sys, string
+import pythoncom
+
+import win32api
+from win32com.adsi import *
+
+verbose_level = 0
+
+server = '' # Must have trailing /
+local_name = win32api.GetComputerName()
+
+def DumpRoot():
+ "Dumps the root DSE"
+ path = "LDAP://%srootDSE" % server
+ rootdse = ADsGetObject(path)
+
+ for item in rootdse.Get("SupportedLDAPVersion"):
+ print("%s supports ldap version %s" % (path, item))
+
+ attributes = ["CurrentTime", "defaultNamingContext"]
+ for attr in attributes:
+ val = rootdse.Get(attr)
+ print(" %s=%s" % (attr, val))
+
+###############################################
+#
+# Code taken from article titled:
+# Reading attributeSchema and classSchema Objects
+def _DumpClass(child):
+ attrs = "Abstract lDAPDisplayName schemaIDGUID schemaNamingContext attributeSyntax oMSyntax"
+ _DumpTheseAttributes(child, string.split(attrs))
+
+def _DumpAttribute(child):
+ attrs = "lDAPDisplayName schemaIDGUID adminDescription adminDisplayName rDNAttID defaultHidingValue defaultObjectCategory systemOnly defaultSecurityDescriptor"
+ _DumpTheseAttributes(child, string.split(attrs))
+
+def _DumpTheseAttributes(child, attrs):
+ for attr in attrs:
+ try:
+ val = child.Get(attr)
+ except pythoncom.com_error as details:
+ continue
+ # ###
+ (hr, msg, exc, arg) = details
+ if exc and exc[2]: msg = exc[2]
+ val = "" % (msg,)
+ if verbose_level >= 2:
+ print(" %s: %s=%s" % (child.Class, attr, val))
+
+def DumpSchema():
+ "Dumps the default DSE schema"
+ # Bind to rootDSE to get the schemaNamingContext property.
+ path = "LDAP://%srootDSE" % server
+ rootdse = ADsGetObject(path)
+ name = rootdse.Get("schemaNamingContext")
+
+ # Bind to the actual schema container.
+ path= "LDAP://" + server + name
+ print("Binding to", path)
+ ob = ADsGetObject(path)
+ nclasses = nattr = nsub = nunk = 0
+
+ # Enumerate the attribute and class objects in the schema container.
+ for child in ob:
+ # Find out if this is a class, attribute, or subSchema object.
+ class_name = child.Class
+ if class_name == "classSchema":
+ _DumpClass(child)
+ nclasses = nclasses + 1
+ elif class_name == "attributeSchema":
+ _DumpAttribute(child)
+ nattr = nattr + 1
+ elif class_name == "subSchema":
+ nsub = nsub + 1
+ else:
+ print("Unknown class:", class_name)
+ nunk = nunk + 1
+ if verbose_level:
+ print("Processed", nclasses, "classes")
+ print("Processed", nattr, "attributes")
+ print("Processed", nsub, "sub-schema's")
+ print("Processed", nunk, "unknown types")
+
+def _DumpObject(ob, level = 0):
+ prefix = " " * level
+ print("%s%s object: %s" % (prefix, ob.Class, ob.Name))
+ # Do the directory object thing
+ try:
+ dir_ob = ADsGetObject(ob.ADsPath, IID_IDirectoryObject)
+ except pythoncom.com_error:
+ dir_ob = None
+ if dir_ob is not None:
+ info = dir_ob.GetObjectInformation()
+ print("%s RDN='%s', ObjectDN='%s'" % (prefix, info.RDN, info.ObjectDN))
+ # Create a list of names to fetch
+ names = ["distinguishedName"]
+ attrs = dir_ob.GetObjectAttributes(names)
+ for attr in attrs:
+ for val, typ in attr.Values:
+ print("%s Attribute '%s' = %s" % (prefix, attr.AttrName, val))
+
+ for child in ob:
+ _DumpObject(child, level+1)
+
+def DumpAllObjects():
+ "Recursively dump the entire directory!"
+ path = "LDAP://%srootDSE" % server
+ rootdse = ADsGetObject(path)
+ name = rootdse.Get("defaultNamingContext")
+
+ # Bind to the actual schema container.
+ path= "LDAP://" + server + name
+ print("Binding to", path)
+ ob = ADsGetObject(path)
+
+ # Enumerate the attribute and class objects in the schema container.
+ _DumpObject(ob)
+
+##########################################################
+#
+# Code taken from article:
+# Example Code for Enumerating Schema Classes, Attributes, and Syntaxes
+
+# Fill a map with VT_ datatypes, to give us better names:
+vt_map = {}
+for name, val in pythoncom.__dict__.items():
+ if name[:3] == "VT_":
+ vt_map[val] = name
+
+def DumpSchema2():
+ "Dumps the schema using an alternative technique"
+ path = "LDAP://%sschema" % (server,)
+ schema = ADsGetObject(path, IID_IADsContainer)
+ nclass = nprop = nsyntax = 0
+ for item in schema:
+ item_class = string.lower(item.Class)
+ if item_class == "class":
+ items = []
+ if item.Abstract: items.append("Abstract")
+ if item.Auxiliary: items.append("Auxiliary")
+# if item.Structural: items.append("Structural")
+ desc = string.join(items, ", ")
+ import win32com.util
+ iid_name = win32com.util.IIDToInterfaceName(item.PrimaryInterface)
+ if verbose_level >= 2:
+ print("Class: Name=%s, Flags=%s, Primary Interface=%s" % (item.Name, desc, iid_name))
+ nclass = nclass + 1
+ elif item_class == "property":
+ if item.MultiValued:
+ val_type = "Multi-Valued"
+ else:
+ val_type = "Single-Valued"
+ if verbose_level >= 2:
+ print("Property: Name=%s, %s" % (item.Name, val_type))
+ nprop = nprop + 1
+ elif item_class == "syntax":
+ data_type = vt_map.get(item.OleAutoDataType, "")
+ if verbose_level >= 2:
+ print("Syntax: Name=%s, Datatype = %s" % (item.Name, data_type))
+ nsyntax = nsyntax + 1
+ if verbose_level >= 1:
+ print("Processed", nclass, "classes")
+ print("Processed", nprop, "properties")
+ print("Processed", nsyntax, "syntax items")
+
+def DumpGC():
+ "Dumps the GC: object (whatever that is!)"
+ ob = ADsGetObject("GC:", IID_IADsContainer)
+ for sub_ob in ob:
+ print("GC ob: %s (%s)" % (sub_ob.Name, sub_ob.ADsPath))
+
+def DumpLocalUsers():
+ "Dumps the local machine users"
+ path = "WinNT://%s,computer" % (local_name,)
+ ob = ADsGetObject(path, IID_IADsContainer)
+ ob.put_Filter(["User", "Group"])
+ for sub_ob in ob:
+ print("User/Group: %s (%s)" % (sub_ob.Name, sub_ob.ADsPath))
+
+def DumpLocalGroups():
+ "Dumps the local machine groups"
+ path = "WinNT://%s,computer" % (local_name,)
+ ob = ADsGetObject(path, IID_IADsContainer)
+
+ ob.put_Filter(["Group"])
+ for sub_ob in ob:
+ print("Group: %s (%s)" % (sub_ob.Name, sub_ob.ADsPath))
+ # get the members
+ members = sub_ob.Members()
+ for member in members:
+ print(" Group member: %s (%s)" % (member.Name, member.ADsPath))
+
+def usage(tests):
+ import os
+ print("Usage: %s [-s server ] [-v] [Test ...]" % os.path.basename(sys.argv[0]))
+ print(" -v : Verbose - print more information")
+ print(" -s : server - execute the tests against the named server")
+ print("where Test is one of:")
+ for t in tests:
+ print(t.__name__,":", t.__doc__)
+ print()
+ print("If not tests are specified, all tests are run")
+ sys.exit(1)
+
+def main():
+ import getopt, traceback
+ tests = []
+ for ob in globals().values():
+ if type(ob)==type(main) and ob.__doc__:
+ tests.append(ob)
+ opts, args = getopt.getopt(sys.argv[1:], "s:hv")
+ for opt, val in opts:
+ if opt=="-s":
+ if val[-1] not in "\\/":
+ val = val + "/"
+ global server
+ server = val
+ if opt=="-h":
+ usage(tests)
+ if opt=="-v":
+ global verbose_level
+ verbose_level = verbose_level + 1
+
+ if len(args)==0:
+ print("Running all tests - use '-h' to see command-line options...")
+ dotests = tests
+ else:
+ dotests = []
+ for arg in args:
+ for t in tests:
+ if t.__name__==arg:
+ dotests.append(t)
+ break
+ else:
+ print("Test '%s' unknown - skipping" % arg)
+ if not len(dotests):
+ print("Nothing to do!")
+ usage(tests)
+ for test in dotests:
+ try:
+ test()
+ except:
+ print("Test %s failed" % test.__name__)
+ traceback.print_exc()
+
+if __name__=='__main__':
+ main()
\ No newline at end of file
diff --git a/Lib/site-packages/win32comext/authorization/__init__.py b/Lib/site-packages/win32comext/authorization/__init__.py
new file mode 100644
index 0000000..f18a9d6
--- /dev/null
+++ b/Lib/site-packages/win32comext/authorization/__init__.py
@@ -0,0 +1,6 @@
+# This is a python package
+# __PackageSupportBuildPath__ not needed for distutil based builds,
+# but not everyone is there yet.
+import win32com
+win32com.__PackageSupportBuildPath__(__path__)
+
diff --git a/Lib/site-packages/win32comext/authorization/authorization.pyd b/Lib/site-packages/win32comext/authorization/authorization.pyd
new file mode 100644
index 0000000..07a9ca5
Binary files /dev/null and b/Lib/site-packages/win32comext/authorization/authorization.pyd differ
diff --git a/Lib/site-packages/win32comext/authorization/demos/EditSecurity.py b/Lib/site-packages/win32comext/authorization/demos/EditSecurity.py
new file mode 100644
index 0000000..468e98d
--- /dev/null
+++ b/Lib/site-packages/win32comext/authorization/demos/EditSecurity.py
@@ -0,0 +1,140 @@
+import os
+import win32com.server.policy
+import win32security, ntsecuritycon, win32con
+import pythoncom, win32api
+from win32com.authorization import authorization
+
+from ntsecuritycon import FILE_READ_ATTRIBUTES, FILE_READ_DATA, FILE_READ_EA, SYNCHRONIZE,\
+ STANDARD_RIGHTS_READ, STANDARD_RIGHTS_WRITE, STANDARD_RIGHTS_EXECUTE, FILE_APPEND_DATA, \
+ FILE_WRITE_ATTRIBUTES, FILE_WRITE_DATA, FILE_WRITE_EA, WRITE_OWNER, WRITE_DAC, READ_CONTROL, \
+ SI_ADVANCED, SI_EDIT_AUDITS, SI_EDIT_PROPERTIES, SI_EDIT_ALL, SI_PAGE_TITLE, SI_RESET, \
+ SI_ACCESS_SPECIFIC, SI_ACCESS_GENERAL, SI_ACCESS_CONTAINER, SI_ACCESS_PROPERTY, \
+ FILE_ALL_ACCESS, FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE, \
+ OBJECT_INHERIT_ACE, CONTAINER_INHERIT_ACE, INHERIT_ONLY_ACE, \
+ SI_PAGE_PERM, SI_PAGE_ADVPERM, SI_PAGE_AUDIT, SI_PAGE_OWNER, PSPCB_SI_INITDIALOG, \
+ SI_CONTAINER
+from win32security import OBJECT_INHERIT_ACE, CONTAINER_INHERIT_ACE, INHERIT_ONLY_ACE
+from win32com.shell.shellcon import PSPCB_RELEASE, PSPCB_CREATE ## Msg parameter to PropertySheetPageCallback
+from pythoncom import IID_NULL
+
+
+class SecurityInformation(win32com.server.policy.DesignatedWrapPolicy):
+ _com_interfaces_=[authorization.IID_ISecurityInformation]
+ _public_methods_=['GetObjectInformation','GetSecurity','SetSecurity','GetAccessRights',
+ 'GetInheritTypes','MapGeneric','PropertySheetPageCallback']
+
+ def __init__(self, FileName):
+ self.FileName=FileName
+ self._wrap_(self)
+
+ def GetObjectInformation(self):
+ """Identifies object whose security will be modified, and determines options available
+ to the end user"""
+ flags=SI_ADVANCED|SI_EDIT_ALL|SI_PAGE_TITLE|SI_RESET
+ if os.path.isdir(self.FileName):
+ flags|=SI_CONTAINER
+ hinstance=0 ## handle to module containing string resources
+ servername='' ## name of authenticating server if not local machine
+ objectname=os.path.split(self.FileName)[1]
+ pagetitle='Python ACL Editor'
+ if os.path.isdir(self.FileName):
+ pagetitle+=' (dir)'
+ else:
+ pagetitle+=' (file)'
+ objecttype=IID_NULL
+ return flags, hinstance, servername, objectname, pagetitle, objecttype
+
+ def GetSecurity(self, requestedinfo, bdefault):
+ """Requests the existing permissions for object"""
+ if bdefault:
+ ## This is invoked if the 'Default' button is pressed (only present if SI_RESET is passed
+ ## with the flags in GetObjectInfo). Passing an empty SD with a NULL Dacl
+ ## should cause inherited ACL from parent dir or default dacl from user's token to be used
+ return win32security.SECURITY_DESCRIPTOR()
+ else:
+ ## GetFileSecurity sometimes fails to return flags indicating that an ACE is inherited
+ return win32security.GetNamedSecurityInfo(self.FileName, win32security.SE_FILE_OBJECT, requestedinfo)
+
+ def SetSecurity(self, requestedinfo, sd):
+ """Applies permissions to the object"""
+ owner=sd.GetSecurityDescriptorOwner()
+ group=sd.GetSecurityDescriptorGroup()
+ dacl=sd.GetSecurityDescriptorDacl()
+ sacl=sd.GetSecurityDescriptorSacl()
+ win32security.SetNamedSecurityInfo(self.FileName, win32security.SE_FILE_OBJECT, requestedinfo,
+ owner, group, dacl, sacl)
+ ## should also handle recursive operations here
+
+ def GetAccessRights(self, objecttype, flags):
+ """Returns a tuple of (AccessRights, DefaultAccess), where AccessRights is a sequence of tuples representing
+ SI_ACCESS structs, containing (guid, access mask, Name, flags). DefaultAccess indicates which of the
+ AccessRights will be used initially when a new ACE is added (zero based).
+ Flags can contain SI_ACCESS_SPECIFIC,SI_ACCESS_GENERAL,SI_ACCESS_CONTAINER,SI_ACCESS_PROPERTY,
+ CONTAINER_INHERIT_ACE,INHERIT_ONLY_ACE,OBJECT_INHERIT_ACE
+ """
+ ## input flags: SI_ADVANCED,SI_EDIT_AUDITS,SI_EDIT_PROPERTIES indicating which property sheet is requesting the rights
+ if (objecttype is not None) and (objecttype!=IID_NULL):
+ ## Should not be true for file objects. Usually only used with DS objects that support security for
+ ## their properties
+ raise NotImplementedError("Object type is not supported")
+
+ if os.path.isdir(self.FileName):
+ file_append_data_desc='Create subfolders'
+ file_write_data_desc='Create Files'
+ else:
+ file_append_data_desc='Append data'
+ file_write_data_desc='Write data'
+
+ accessrights=[(IID_NULL, FILE_GENERIC_READ, 'Generic read', SI_ACCESS_GENERAL|SI_ACCESS_SPECIFIC|OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE),
+ (IID_NULL, FILE_GENERIC_WRITE, 'Generic write', SI_ACCESS_GENERAL|SI_ACCESS_SPECIFIC|OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE),
+ (IID_NULL, win32con.DELETE, 'Delete', SI_ACCESS_SPECIFIC|OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE),
+ (IID_NULL, WRITE_OWNER, 'Change owner', SI_ACCESS_SPECIFIC|OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE),
+ (IID_NULL, READ_CONTROL,'Read Permissions', SI_ACCESS_SPECIFIC|OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE),
+ (IID_NULL, WRITE_DAC, 'Change permissions', SI_ACCESS_SPECIFIC|OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE),
+ (IID_NULL, FILE_APPEND_DATA, file_append_data_desc, SI_ACCESS_SPECIFIC|OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE),
+ (IID_NULL, FILE_WRITE_DATA, file_write_data_desc, SI_ACCESS_SPECIFIC|OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE)
+ ]
+ return (accessrights, 0)
+
+ def MapGeneric(self, guid, aceflags, mask):
+ """ Converts generic access rights to specific rights. This implementation uses standard file system rights,
+ but you can map them any way that suits your application.
+ """
+ return win32security.MapGenericMask(mask, (FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE, FILE_ALL_ACCESS))
+
+ def GetInheritTypes(self):
+ """Specifies which types of ACE inheritance are supported.
+ Returns a sequence of tuples representing SI_INHERIT_TYPE structs, containing
+ (object type guid, inheritance flags, display name). Guid is usually only used with
+ Directory Service objects.
+ """
+ return ((IID_NULL, 0, 'Only current object'),
+ (IID_NULL, OBJECT_INHERIT_ACE, 'Files inherit permissions'),
+ (IID_NULL, CONTAINER_INHERIT_ACE, 'Sub Folders inherit permissions'),
+ (IID_NULL, CONTAINER_INHERIT_ACE|OBJECT_INHERIT_ACE, 'Files and subfolders'),
+ )
+
+ def PropertySheetPageCallback(self, hwnd, msg, pagetype):
+ """Invoked each time a property sheet page is created or destroyed."""
+ ## page types from SI_PAGE_TYPE enum: SI_PAGE_PERM SI_PAGE_ADVPERM SI_PAGE_AUDIT SI_PAGE_OWNER
+ ## msg: PSPCB_CREATE, PSPCB_RELEASE, PSPCB_SI_INITDIALOG
+ return None
+
+ def EditSecurity(self, owner_hwnd=0):
+ """Creates an ACL editor dialog based on parameters returned by interface methods"""
+ isi=pythoncom.WrapObject(self, authorization.IID_ISecurityInformation, pythoncom.IID_IUnknown)
+ authorization.EditSecurity(owner_hwnd, isi)
+
+## folder permissions
+temp_dir=win32api.GetTempPath()
+dir_name=win32api.GetTempFileName(temp_dir,'isi')[0]
+print(dir_name)
+os.remove(dir_name)
+os.mkdir(dir_name)
+si=SecurityInformation(dir_name)
+si.EditSecurity()
+
+## file permissions
+fname=win32api.GetTempFileName(dir_name,'isi')[0]
+si=SecurityInformation(fname)
+si.EditSecurity()
diff --git a/Lib/site-packages/win32comext/authorization/demos/EditServiceSecurity.py b/Lib/site-packages/win32comext/authorization/demos/EditServiceSecurity.py
new file mode 100644
index 0000000..e3dfd5d
--- /dev/null
+++ b/Lib/site-packages/win32comext/authorization/demos/EditServiceSecurity.py
@@ -0,0 +1,134 @@
+"""
+Implements a permissions editor for services.
+Service can be specified as plain name for local machine,
+or as a remote service of the form \\machinename\service
+"""
+
+import os
+import win32com.server.policy
+import win32security, ntsecuritycon, win32con
+import pythoncom, win32api, win32service
+from win32com.authorization import authorization
+
+SERVICE_GENERIC_EXECUTE=win32service.SERVICE_START|win32service.SERVICE_STOP|win32service.SERVICE_PAUSE_CONTINUE|win32service.SERVICE_USER_DEFINED_CONTROL
+SERVICE_GENERIC_READ=win32service.SERVICE_QUERY_CONFIG|win32service.SERVICE_QUERY_STATUS|win32service.SERVICE_INTERROGATE|win32service.SERVICE_ENUMERATE_DEPENDENTS
+SERVICE_GENERIC_WRITE=win32service.SERVICE_CHANGE_CONFIG
+
+from ntsecuritycon import STANDARD_RIGHTS_READ, STANDARD_RIGHTS_WRITE, STANDARD_RIGHTS_EXECUTE, \
+ WRITE_OWNER, WRITE_DAC, READ_CONTROL, \
+ SI_ADVANCED, SI_EDIT_AUDITS, SI_EDIT_PROPERTIES, SI_EDIT_ALL, SI_PAGE_TITLE, SI_RESET, \
+ SI_ACCESS_SPECIFIC, SI_ACCESS_GENERAL, SI_ACCESS_CONTAINER, SI_ACCESS_PROPERTY, \
+ OBJECT_INHERIT_ACE, CONTAINER_INHERIT_ACE, INHERIT_ONLY_ACE, \
+ SI_PAGE_PERM, SI_PAGE_ADVPERM, SI_PAGE_AUDIT, SI_PAGE_OWNER, PSPCB_SI_INITDIALOG, \
+ SI_CONTAINER
+from win32security import OBJECT_INHERIT_ACE, CONTAINER_INHERIT_ACE, INHERIT_ONLY_ACE
+from win32com.shell.shellcon import PSPCB_RELEASE, PSPCB_CREATE ## Msg parameter to PropertySheetPageCallback
+from pythoncom import IID_NULL
+
+
+class ServiceSecurity(win32com.server.policy.DesignatedWrapPolicy):
+ _com_interfaces_=[authorization.IID_ISecurityInformation]
+ _public_methods_=['GetObjectInformation','GetSecurity','SetSecurity','GetAccessRights',
+ 'GetInheritTypes','MapGeneric','PropertySheetPageCallback']
+
+ def __init__(self, ServiceName):
+ self.ServiceName=ServiceName
+ self._wrap_(self)
+
+ def GetObjectInformation(self):
+ """Identifies object whose security will be modified, and determines options available
+ to the end user"""
+ flags=SI_ADVANCED|SI_EDIT_ALL|SI_PAGE_TITLE|SI_RESET
+ hinstance=0 ## handle to module containing string resources
+ servername='' ## name of authenticating server if not local machine
+
+ ## service name can contain remote machine name of the form \\Server\ServiceName
+ objectname=os.path.split(self.ServiceName)[1]
+ pagetitle='Service Permissions for '+self.ServiceName
+ objecttype=IID_NULL
+ return flags, hinstance, servername, objectname, pagetitle, objecttype
+
+ def GetSecurity(self, requestedinfo, bdefault):
+ """Requests the existing permissions for object"""
+ if bdefault:
+ return win32security.SECURITY_DESCRIPTOR()
+ else:
+ return win32security.GetNamedSecurityInfo(self.ServiceName, win32security.SE_SERVICE, requestedinfo)
+
+ def SetSecurity(self, requestedinfo, sd):
+ """Applies permissions to the object"""
+ owner=sd.GetSecurityDescriptorOwner()
+ group=sd.GetSecurityDescriptorGroup()
+ dacl=sd.GetSecurityDescriptorDacl()
+ sacl=sd.GetSecurityDescriptorSacl()
+ win32security.SetNamedSecurityInfo(self.ServiceName, win32security.SE_SERVICE, requestedinfo,
+ owner, group, dacl, sacl)
+
+ def GetAccessRights(self, objecttype, flags):
+ """Returns a tuple of (AccessRights, DefaultAccess), where AccessRights is a sequence of tuples representing
+ SI_ACCESS structs, containing (guid, access mask, Name, flags). DefaultAccess indicates which of the
+ AccessRights will be used initially when a new ACE is added (zero based).
+ Flags can contain SI_ACCESS_SPECIFIC,SI_ACCESS_GENERAL,SI_ACCESS_CONTAINER,SI_ACCESS_PROPERTY,
+ CONTAINER_INHERIT_ACE,INHERIT_ONLY_ACE,OBJECT_INHERIT_ACE
+ """
+ ## input flags: SI_ADVANCED,SI_EDIT_AUDITS,SI_EDIT_PROPERTIES indicating which property sheet is requesting the rights
+ if (objecttype is not None) and (objecttype!=IID_NULL):
+ ## Not relevent for services
+ raise NotImplementedError("Object type is not supported")
+
+ ## ???? for some reason, the DACL for a service will not retain ACCESS_SYSTEM_SECURITY in an ACE ????
+ ## (IID_NULL, win32con.ACCESS_SYSTEM_SECURITY, 'View/change audit settings', SI_ACCESS_SPECIFIC),
+
+ accessrights=[
+ (IID_NULL, win32service.SERVICE_ALL_ACCESS, 'Full control', SI_ACCESS_GENERAL),
+ (IID_NULL, SERVICE_GENERIC_READ, 'Generic read', SI_ACCESS_GENERAL),
+ (IID_NULL, SERVICE_GENERIC_WRITE, 'Generic write', SI_ACCESS_GENERAL),
+ (IID_NULL, SERVICE_GENERIC_EXECUTE, 'Start/Stop/Pause service', SI_ACCESS_GENERAL),
+ (IID_NULL, READ_CONTROL, 'Read Permissions', SI_ACCESS_GENERAL),
+ (IID_NULL, WRITE_DAC, 'Change permissions', SI_ACCESS_GENERAL),
+ (IID_NULL, WRITE_OWNER, 'Change owner', SI_ACCESS_GENERAL),
+ (IID_NULL, win32con.DELETE, 'Delete service', SI_ACCESS_GENERAL),
+ (IID_NULL, win32service.SERVICE_START, 'Start service', SI_ACCESS_SPECIFIC),
+ (IID_NULL, win32service.SERVICE_STOP, 'Stop service', SI_ACCESS_SPECIFIC),
+ (IID_NULL, win32service.SERVICE_PAUSE_CONTINUE, 'Pause/unpause service', SI_ACCESS_SPECIFIC),
+ (IID_NULL, win32service.SERVICE_USER_DEFINED_CONTROL, 'Execute user defined operations', SI_ACCESS_SPECIFIC),
+ (IID_NULL, win32service.SERVICE_QUERY_CONFIG, 'Read configuration', SI_ACCESS_SPECIFIC),
+ (IID_NULL, win32service.SERVICE_CHANGE_CONFIG, 'Change configuration', SI_ACCESS_SPECIFIC),
+ (IID_NULL, win32service.SERVICE_ENUMERATE_DEPENDENTS, 'List dependent services', SI_ACCESS_SPECIFIC),
+ (IID_NULL, win32service.SERVICE_QUERY_STATUS, 'Query status', SI_ACCESS_SPECIFIC),
+ (IID_NULL, win32service.SERVICE_INTERROGATE, 'Query status (immediate)', SI_ACCESS_SPECIFIC),
+ ]
+ return (accessrights, 0)
+
+ def MapGeneric(self, guid, aceflags, mask):
+ """ Converts generic access rights to specific rights.
+ """
+ return win32security.MapGenericMask(mask,
+ (SERVICE_GENERIC_READ, SERVICE_GENERIC_WRITE, SERVICE_GENERIC_EXECUTE, win32service.SERVICE_ALL_ACCESS))
+
+ def GetInheritTypes(self):
+ """Specifies which types of ACE inheritance are supported.
+ Services don't use any inheritance
+ """
+ return ((IID_NULL, 0, 'Only current object'),)
+
+ def PropertySheetPageCallback(self, hwnd, msg, pagetype):
+ """Invoked each time a property sheet page is created or destroyed."""
+ ## page types from SI_PAGE_TYPE enum: SI_PAGE_PERM SI_PAGE_ADVPERM SI_PAGE_AUDIT SI_PAGE_OWNER
+ ## msg: PSPCB_CREATE, PSPCB_RELEASE, PSPCB_SI_INITDIALOG
+ return None
+
+ def EditSecurity(self, owner_hwnd=0):
+ """Creates an ACL editor dialog based on parameters returned by interface methods"""
+ isi=pythoncom.WrapObject(self, authorization.IID_ISecurityInformation, pythoncom.IID_IUnknown)
+ authorization.EditSecurity(owner_hwnd, isi)
+
+if __name__=='__main__':
+ # Find the first service on local machine and edit its permissions
+ scm = win32service.OpenSCManager(None, None, win32service.SC_MANAGER_ENUMERATE_SERVICE)
+ svcs=win32service.EnumServicesStatus(scm)
+ win32service.CloseServiceHandle(scm)
+ si=ServiceSecurity(svcs[0][0])
+ si.EditSecurity()
+
+
diff --git a/Lib/site-packages/win32comext/axcontrol/__init__.py b/Lib/site-packages/win32comext/axcontrol/__init__.py
new file mode 100644
index 0000000..53cc08f
--- /dev/null
+++ b/Lib/site-packages/win32comext/axcontrol/__init__.py
@@ -0,0 +1,4 @@
+# See if we have a special directory for the binaries (for developers)
+import win32com
+win32com.__PackageSupportBuildPath__(__path__)
+
diff --git a/Lib/site-packages/win32comext/axcontrol/axcontrol.pyd b/Lib/site-packages/win32comext/axcontrol/axcontrol.pyd
new file mode 100644
index 0000000..f2080e9
Binary files /dev/null and b/Lib/site-packages/win32comext/axcontrol/axcontrol.pyd differ
diff --git a/Lib/site-packages/win32comext/axdebug/__init__.py b/Lib/site-packages/win32comext/axdebug/__init__.py
new file mode 100644
index 0000000..1253a5d
--- /dev/null
+++ b/Lib/site-packages/win32comext/axdebug/__init__.py
@@ -0,0 +1,3 @@
+# See if we have a special directory for the binaries (for developers)
+import win32com
+win32com.__PackageSupportBuildPath__(__path__)
diff --git a/Lib/site-packages/win32comext/axdebug/adb.py b/Lib/site-packages/win32comext/axdebug/adb.py
new file mode 100644
index 0000000..9d68a79
--- /dev/null
+++ b/Lib/site-packages/win32comext/axdebug/adb.py
@@ -0,0 +1,416 @@
+"""The glue between the Python debugger interface and the Active Debugger interface
+"""
+from win32com.axdebug.util import trace, _wrap, _wrap_remove
+from win32com.server.util import unwrap
+import win32com.client.connect
+from . import gateways
+import sys, bdb, traceback
+import axdebug, stackframe
+import win32api, pythoncom
+import _thread, os
+
+def fnull(*args):
+ pass
+
+try:
+ os.environ["DEBUG_AXDEBUG"]
+ debugging = 1
+except KeyError:
+ debugging = 0
+
+traceenter = fnull # trace enter of functions
+tracev = fnull # verbose trace
+
+if debugging:
+ traceenter = trace # trace enter of functions
+ tracev = trace # verbose trace
+
+class OutputReflector:
+ def __init__(self, file, writefunc):
+ self.writefunc = writefunc
+ self.file = file
+ def __getattr__(self,name):
+ return getattr(self.file, name)
+ def write(self,message):
+ self.writefunc(message)
+ self.file.write(message)
+
+def _dumpf(frame):
+ if frame is None:
+ return ""
+ else:
+ addn = "(with trace!)"
+ if frame.f_trace is None:
+ addn = " **No Trace Set **"
+ return "Frame at %d, file %s, line: %d%s" % (id(frame), frame.f_code.co_filename, frame.f_lineno, addn)
+
+g_adb = None
+
+def OnSetBreakPoint(codeContext, breakPointState, lineNo):
+ try:
+ fileName = codeContext.codeContainer.GetFileName()
+ # inject the code into linecache.
+ import linecache
+ linecache.cache[fileName] = 0, 0, codeContext.codeContainer.GetText(), fileName
+ g_adb._OnSetBreakPoint(fileName, codeContext, breakPointState, lineNo+1)
+ except:
+ traceback.print_exc()
+
+class Adb(bdb.Bdb,gateways.RemoteDebugApplicationEvents):
+ def __init__(self):
+ self.debugApplication = None
+ self.debuggingThread = None
+ self.debuggingThreadStateHandle = None
+ self.stackSnifferCookie = self.stackSniffer = None
+ self.codeContainerProvider = None
+ self.debuggingThread = None
+ self.breakFlags = None
+ self.breakReason = None
+ self.appDebugger = None
+ self.appEventConnection = None
+ self.logicalbotframe = None # Anything at this level or below does not exist!
+ self.currentframe = None # The frame we are currently in.
+ self.recursiveData = [] # Data saved for each reentery on this thread.
+ bdb.Bdb.__init__(self)
+ self._threadprotectlock = _thread.allocate_lock()
+ self.reset()
+
+ def canonic(self, fname):
+ if fname[0]=='<':
+ return fname
+ return bdb.Bdb.canonic(self, fname)
+
+ def reset(self):
+ traceenter("adb.reset")
+ bdb.Bdb.reset(self)
+
+ def __xxxxx__set_break(self, filename, lineno, cond = None):
+ # As per standard one, except no linecache checking!
+ if filename not in self.breaks:
+ self.breaks[filename] = []
+ list = self.breaks[filename]
+ if lineno in list:
+ return 'There is already a breakpoint there!'
+ list.append(lineno)
+ if cond is not None: self.cbreaks[filename, lineno]=cond
+
+ def stop_here(self, frame):
+ traceenter("stop_here", _dumpf(frame), _dumpf(self.stopframe))
+ # As per bdb.stop_here, except for logicalbotframe
+## if self.stopframe is None:
+## return 1
+ if frame is self.stopframe:
+ return 1
+
+ tracev("stop_here said 'No'!")
+ return 0
+
+ def break_here(self, frame):
+ traceenter("break_here", self.breakFlags, _dumpf(frame))
+ self.breakReason = None
+ if self.breakFlags==axdebug.APPBREAKFLAG_DEBUGGER_HALT:
+ self.breakReason = axdebug.BREAKREASON_DEBUGGER_HALT
+ elif self.breakFlags==axdebug.APPBREAKFLAG_DEBUGGER_BLOCK:
+ self.breakReason = axdebug.BREAKREASON_DEBUGGER_BLOCK
+ elif self.breakFlags==axdebug.APPBREAKFLAG_STEP:
+ self.breakReason = axdebug.BREAKREASON_STEP
+ else:
+ print("Calling base 'break_here' with", self.breaks)
+ if bdb.Bdb.break_here(self, frame):
+ self.breakReason = axdebug.BREAKREASON_BREAKPOINT
+ return self.breakReason is not None
+
+ def break_anywhere(self, frame):
+ traceenter("break_anywhere", _dumpf(frame))
+ if self.breakFlags==axdebug.APPBREAKFLAG_DEBUGGER_HALT:
+ self.breakReason = axdebug.BREAKREASON_DEBUGGER_HALT
+ return 1
+ rc = bdb.Bdb.break_anywhere(self, frame)
+ tracev("break_anywhere",_dumpf(frame),"returning",rc)
+ return rc
+
+ def dispatch_return(self, frame, arg):
+ traceenter("dispatch_return", _dumpf(frame), arg)
+ if self.logicalbotframe is frame:
+ # We dont want to debug parent frames.
+ tracev("dispatch_return resetting sys.trace")
+ sys.settrace(None)
+ return
+# self.bSetTrace = 0
+ self.currentframe = frame.f_back
+ return bdb.Bdb.dispatch_return(self, frame, arg)
+
+ def dispatch_line(self, frame):
+ traceenter("dispatch_line", _dumpf(frame), _dumpf(self.botframe))
+# trace("logbotframe is", _dumpf(self.logicalbotframe), "botframe is", self.botframe)
+ if frame is self.logicalbotframe:
+ trace("dispatch_line", _dumpf(frame), "for bottom frame returing tracer")
+ # The next code executed in the frame above may be a builtin (eg, apply())
+ # in which sys.trace needs to be set.
+ sys.settrace(self.trace_dispatch)
+ # And return the tracer incase we are about to execute Python code,
+ # in which case sys tracer is ignored!
+ return self.trace_dispatch
+
+ if self.codeContainerProvider.FromFileName(frame.f_code.co_filename) is None:
+ trace("dispatch_line has no document for", _dumpf(frame), "- skipping trace!")
+ return None
+ self.currentframe = frame # So the stack sniffer knows our most recent, debuggable code.
+ return bdb.Bdb.dispatch_line(self, frame)
+
+ def dispatch_call(self, frame, arg):
+ traceenter("dispatch_call",_dumpf(frame))
+ frame.f_locals['__axstack_address__'] = axdebug.GetStackAddress()
+ if frame is self.botframe:
+ trace("dispatch_call is self.botframe - returning tracer")
+ return self.trace_dispatch
+ # Not our bottom frame. If we have a document for it,
+ # then trace it, otherwise run at full speed.
+ if self.codeContainerProvider.FromFileName(frame.f_code.co_filename) is None:
+ trace("dispatch_call has no document for", _dumpf(frame), "- skipping trace!")
+## sys.settrace(None)
+ return None
+ return self.trace_dispatch
+
+# rc = bdb.Bdb.dispatch_call(self, frame, arg)
+# trace("dispatch_call", _dumpf(frame),"returned",rc)
+# return rc
+
+ def trace_dispatch(self, frame, event, arg):
+ traceenter("trace_dispatch", _dumpf(frame), event, arg)
+ if self.debugApplication is None:
+ trace("trace_dispatch has no application!")
+ return # None
+ return bdb.Bdb.trace_dispatch(self, frame, event, arg)
+
+
+ #
+ # The user functions do bugger all!
+ #
+# def user_call(self, frame, argument_list):
+# traceenter("user_call",_dumpf(frame))
+
+ def user_line(self, frame):
+ traceenter("user_line",_dumpf(frame))
+ # Traces at line zero
+ if frame.f_lineno!=0:
+ breakReason = self.breakReason
+ if breakReason is None:
+ breakReason = axdebug.BREAKREASON_STEP
+ self._HandleBreakPoint(frame, None, breakReason)
+
+ def user_return(self, frame, return_value):
+# traceenter("user_return",_dumpf(frame),return_value)
+ bdb.Bdb.user_return(self, frame, return_value)
+
+ def user_exception(self, frame, exc_info):
+# traceenter("user_exception")
+ bdb.Bdb.user_exception(self, frame, exc_info)
+
+
+ def _HandleBreakPoint(self, frame, tb, reason):
+ traceenter("Calling HandleBreakPoint with reason", reason,"at frame", _dumpf(frame))
+ traceenter(" Current frame is", _dumpf(self.currentframe))
+ try:
+ resumeAction = self.debugApplication.HandleBreakPoint(reason)
+ tracev("HandleBreakPoint returned with ", resumeAction)
+ except pythoncom.com_error as details:
+ # Eeek - the debugger is dead, or something serious is happening.
+ # Assume we should continue
+ resumeAction = axdebug.BREAKRESUMEACTION_CONTINUE
+ trace("HandleBreakPoint FAILED with", details)
+
+ self.stack = []
+ self.curindex = 0
+ if resumeAction == axdebug.BREAKRESUMEACTION_ABORT:
+ self.set_quit()
+ elif resumeAction == axdebug.BREAKRESUMEACTION_CONTINUE:
+ tracev("resume action is continue")
+ self.set_continue()
+ elif resumeAction == axdebug.BREAKRESUMEACTION_STEP_INTO:
+ tracev("resume action is step")
+ self.set_step()
+ elif resumeAction == axdebug.BREAKRESUMEACTION_STEP_OVER:
+ tracev("resume action is next")
+ self.set_next(frame)
+ elif resumeAction == axdebug.BREAKRESUMEACTION_STEP_OUT:
+ tracev("resume action is stop out")
+ self.set_return(frame)
+ else:
+ raise ValueError("unknown resume action flags")
+ self.breakReason = None
+
+ def set_trace(self):
+ self.breakReason = axdebug.BREAKREASON_LANGUAGE_INITIATED
+ bdb.Bdb.set_trace(self)
+
+ def CloseApp(self):
+ traceenter("ClosingApp")
+ self.reset()
+ self.logicalbotframe = None
+ if self.stackSnifferCookie is not None:
+ try:
+ self.debugApplication.RemoveStackFrameSniffer(self.stackSnifferCookie)
+
+ except pythoncom.com_error:
+ trace("*** Could not RemoveStackFrameSniffer %d" % (self.stackSnifferCookie))
+ if self.stackSniffer:
+ _wrap_remove(self.stackSniffer)
+ self.stackSnifferCookie = self.stackSniffer = None
+
+ if self.appEventConnection is not None:
+ self.appEventConnection.Disconnect()
+ self.appEventConnection = None
+ self.debugApplication = None
+ self.appDebugger = None
+ if self.codeContainerProvider is not None:
+ self.codeContainerProvider.Close()
+ self.codeContainerProvider = None
+
+ def AttachApp(self, debugApplication, codeContainerProvider):
+# traceenter("AttachApp", debugApplication, codeContainerProvider)
+ self.codeContainerProvider = codeContainerProvider
+ self.debugApplication = debugApplication
+ self.stackSniffer = _wrap(stackframe.DebugStackFrameSniffer(self), axdebug.IID_IDebugStackFrameSniffer)
+ self.stackSnifferCookie = debugApplication.AddStackFrameSniffer(self.stackSniffer)
+# trace("StackFrameSniffer added (%d)" % self.stackSnifferCookie)
+
+ # Connect to the application events.
+ self.appEventConnection = win32com.client.connect.SimpleConnection(self.debugApplication, self, axdebug.IID_IRemoteDebugApplicationEvents)
+
+ def ResetAXDebugging(self):
+ traceenter("ResetAXDebugging", self, "with refcount", len(self.recursiveData))
+ if win32api.GetCurrentThreadId()!=self.debuggingThread:
+ trace("ResetAXDebugging called on other thread")
+ return
+
+ if len(self.recursiveData)==0:
+# print "ResetAXDebugging called for final time."
+ self.logicalbotframe = None
+ self.debuggingThread = None
+ self.currentframe = None
+ self.debuggingThreadStateHandle = None
+ return
+
+ self.logbotframe, self.stopframe, self.currentframe, self.debuggingThreadStateHandle = self.recursiveData[0]
+ self.recursiveData = self.recursiveData[1:]
+
+ def SetupAXDebugging(self, baseFrame = None, userFrame = None):
+ """Get ready for potential debugging. Must be called on the thread
+ that is being debugged.
+ """
+ # userFrame is for non AXScript debugging. This is the first frame of the
+ # users code.
+ if userFrame is None:
+ userFrame = baseFrame
+ else:
+ # We have missed the "dispatch_call" function, so set this up now!
+ userFrame.f_locals['__axstack_address__'] = axdebug.GetStackAddress()
+
+ traceenter("SetupAXDebugging", self)
+ self._threadprotectlock.acquire()
+ try:
+ thisThread = win32api.GetCurrentThreadId()
+ if self.debuggingThread is None:
+ self.debuggingThread = thisThread
+ else:
+ if self.debuggingThread!=thisThread:
+ trace("SetupAXDebugging called on other thread - ignored!")
+ return
+ # push our context.
+ self.recursiveData.insert(0, (self.logicalbotframe,self.stopframe, self.currentframe,self.debuggingThreadStateHandle))
+ finally:
+ self._threadprotectlock.release()
+
+ trace("SetupAXDebugging has base frame as", _dumpf(baseFrame))
+ self.botframe = baseFrame
+ self.stopframe = userFrame
+ self.logicalbotframe = baseFrame
+ self.currentframe = None
+ self.debuggingThreadStateHandle = axdebug.GetThreadStateHandle()
+
+ self._BreakFlagsChanged()
+
+ # RemoteDebugApplicationEvents
+ def OnConnectDebugger(self, appDebugger):
+ traceenter("OnConnectDebugger", appDebugger)
+ self.appDebugger = appDebugger
+ # Reflect output to appDebugger
+ writefunc = lambda s: appDebugger.onDebugOutput(s)
+ sys.stdout = OutputReflector(sys.stdout, writefunc)
+ sys.stderr = OutputReflector(sys.stderr, writefunc)
+
+ def OnDisconnectDebugger(self):
+ traceenter("OnDisconnectDebugger")
+ # Stop reflecting output
+ if isinstance(sys.stdout, OutputReflector):
+ sys.stdout = sys.stdout.file
+ if isinstance(sys.stderr, OutputReflector):
+ sys.stderr = sys.stderr.file
+ self.appDebugger = None
+ self.set_quit()
+
+ def OnSetName(self, name):
+ traceenter("OnSetName", name)
+ def OnDebugOutput(self, string):
+ traceenter("OnDebugOutput", string)
+ def OnClose(self):
+ traceenter("OnClose")
+ def OnEnterBreakPoint(self, rdat):
+ traceenter("OnEnterBreakPoint", rdat)
+ def OnLeaveBreakPoint(self, rdat):
+ traceenter("OnLeaveBreakPoint", rdat)
+ def OnCreateThread(self, rdat):
+ traceenter("OnCreateThread", rdat)
+ def OnDestroyThread(self, rdat):
+ traceenter("OnDestroyThread", rdat)
+ def OnBreakFlagChange(self, abf, rdat):
+ traceenter("Debugger OnBreakFlagChange", abf, rdat)
+ self.breakFlags = abf
+ self._BreakFlagsChanged()
+
+ def _BreakFlagsChanged(self):
+ traceenter("_BreakFlagsChanged to %s with our thread = %s, and debugging thread = %s" % (self.breakFlags, self.debuggingThread, win32api.GetCurrentThreadId()))
+ trace("_BreakFlagsChanged has breaks", self.breaks)
+ # If a request comes on our debugging thread, then do it now!
+# if self.debuggingThread!=win32api.GetCurrentThreadId():
+# return
+
+ if len(self.breaks) or self.breakFlags:
+
+ if self.logicalbotframe:
+ trace("BreakFlagsChange with bot frame", _dumpf(self.logicalbotframe))
+ # We have frames not to be debugged (eg, Scripting engine frames
+ # (sys.settrace will be set when out logicalbotframe is hit -
+ # this may not be the right thing to do, as it may not cause the
+ # immediate break we desire.)
+ self.logicalbotframe.f_trace = self.trace_dispatch
+ else:
+ trace("BreakFlagsChanged, but no bottom frame")
+ if self.stopframe is not None:
+ self.stopframe.f_trace = self.trace_dispatch
+ # If we have the thread-state for the thread being debugged, then
+ # we dynamically set its trace function - it is possible that the thread
+ # being debugged is in a blocked call (eg, a message box) and we
+ # want to hit the debugger the instant we return
+ if self.debuggingThreadStateHandle is not None and \
+ self.breakFlags and \
+ self.debuggingThread != win32api.GetCurrentThreadId():
+ axdebug.SetThreadStateTrace(self.debuggingThreadStateHandle, self.trace_dispatch)
+ def _OnSetBreakPoint(self, key, codeContext, bps, lineNo):
+ traceenter("_OnSetBreakPoint", self, key, codeContext, bps, lineNo)
+ if bps==axdebug.BREAKPOINT_ENABLED:
+ problem = self.set_break(key, lineNo)
+ if problem:
+ print("*** set_break failed -", problem)
+ trace("_OnSetBreakPoint just set BP and has breaks", self.breaks)
+ else:
+ self.clear_break(key, lineNo)
+ self._BreakFlagsChanged()
+ trace("_OnSetBreakPoint leaving with breaks", self.breaks)
+
+def Debugger():
+ global g_adb
+ if g_adb is None:
+ g_adb = Adb()
+ return g_adb
diff --git a/Lib/site-packages/win32comext/axdebug/axdebug.pyd b/Lib/site-packages/win32comext/axdebug/axdebug.pyd
new file mode 100644
index 0000000..49f1efa
Binary files /dev/null and b/Lib/site-packages/win32comext/axdebug/axdebug.pyd differ
diff --git a/Lib/site-packages/win32comext/axdebug/codecontainer.py b/Lib/site-packages/win32comext/axdebug/codecontainer.py
new file mode 100644
index 0000000..c05c536
--- /dev/null
+++ b/Lib/site-packages/win32comext/axdebug/codecontainer.py
@@ -0,0 +1,254 @@
+"""A utility class for a code container.
+
+A code container is a class which holds source code for a debugger. It knows how
+to color the text, and also how to translate lines into offsets, and back.
+"""
+
+import sys
+from win32com.axdebug import axdebug
+import tokenize
+from .util import RaiseNotImpl, _wrap
+
+from win32com.server.exception import Exception
+import win32api, winerror
+from . import contexts
+
+_keywords = {} # set of Python keywords
+for name in """
+ and assert break class continue def del elif else except exec
+ finally for from global if import in is lambda not
+ or pass print raise return try while
+ """.split():
+ _keywords[name] = 1
+
+class SourceCodeContainer:
+ def __init__(self, text, fileName = "", sourceContext = 0, startLineNumber = 0, site = None, debugDocument = None):
+ self.sourceContext = sourceContext # The source context added by a smart host.
+ self.text = text
+ if text:
+ self._buildlines()
+ self.nextLineNo = 0
+ self.fileName = fileName
+ self.codeContexts = {}
+ self.site = site
+ self.startLineNumber = startLineNumber
+ self.debugDocument = None
+ def _Close(self):
+ self.text = self.lines = self.lineOffsets = None
+ self.codeContexts = None
+ self.debugDocument = None
+ self.site = None
+ self.sourceContext = None
+
+ def GetText(self):
+ return self.text
+ def GetName(self, dnt):
+ assert 0, "You must subclass this"
+ def GetFileName(self):
+ return self.fileName
+
+ def GetPositionOfLine(self, cLineNumber):
+ self.GetText() # Prime us.
+ try:
+ return self.lineOffsets[cLineNumber]
+ except IndexError:
+ raise Exception(scode=winerror.S_FALSE)
+ def GetLineOfPosition(self, charPos):
+ self.GetText() # Prime us.
+ lastOffset = 0
+ lineNo = 0
+ for lineOffset in self.lineOffsets[1:]:
+ if lineOffset > charPos:
+ break
+ lastOffset = lineOffset
+ lineNo = lineNo + 1
+ else: # for not broken.
+# print "Cant find", charPos, "in", self.lineOffsets
+ raise Exception(scode=winerror.S_FALSE)
+# print "GLOP ret=",lineNo, (charPos-lastOffset)
+ return lineNo, (charPos-lastOffset)
+
+ def GetNextLine(self):
+ if self.nextLineNo>=len(self.lines):
+ self.nextLineNo = 0 # auto-reset.
+ return ""
+ rc = self.lines[self.nextLineNo]
+ self.nextLineNo = self.nextLineNo + 1
+ return rc
+
+ def GetLine(self, num):
+ self.GetText() # Prime us.
+ return self.lines[num]
+
+ def GetNumChars(self):
+ return len(self.GetText())
+
+ def GetNumLines(self):
+ self.GetText() # Prime us.
+ return len(self.lines)
+
+ def _buildline(self, pos):
+ i = self.text.find('\n', pos)
+ if i < 0:
+ newpos = len(self.text)
+ else:
+ newpos = i+1
+ r = self.text[pos:newpos]
+ return r, newpos
+
+ def _buildlines(self):
+ self.lines = []
+ self.lineOffsets = [0]
+ line, pos = self._buildline(0)
+ while line:
+ self.lines.append(line)
+ self.lineOffsets.append(pos)
+ line, pos = self._buildline(pos)
+
+ def _ProcessToken(self, type, token, spos, epos, line):
+ srow, scol = spos
+ erow, ecol = epos
+ self.GetText() # Prime us.
+ linenum = srow - 1 # Lines zero based for us too.
+ realCharPos = self.lineOffsets[linenum] + scol
+ numskipped = realCharPos - self.lastPos
+ if numskipped==0:
+ pass
+ elif numskipped==1:
+ self.attrs.append(axdebug.SOURCETEXT_ATTR_COMMENT)
+ else:
+ self.attrs.append((axdebug.SOURCETEXT_ATTR_COMMENT, numskipped))
+ kwSize = len(token)
+ self.lastPos = realCharPos + kwSize
+ attr = 0
+
+ if type==tokenize.NAME:
+ if token in _keywords:
+ attr = axdebug.SOURCETEXT_ATTR_KEYWORD
+ elif type==tokenize.STRING:
+ attr = axdebug.SOURCETEXT_ATTR_STRING
+ elif type==tokenize.NUMBER:
+ attr = axdebug.SOURCETEXT_ATTR_NUMBER
+ elif type==tokenize.OP:
+ attr = axdebug.SOURCETEXT_ATTR_OPERATOR
+ elif type==tokenize.COMMENT:
+ attr = axdebug.SOURCETEXT_ATTR_COMMENT
+ # else attr remains zero...
+ if kwSize==0:
+ pass
+ elif kwSize==1:
+ self.attrs.append(attr)
+ else:
+ self.attrs.append((attr, kwSize))
+
+ def GetSyntaxColorAttributes(self):
+ self.lastPos = 0
+ self.attrs = []
+ try:
+ tokenize.tokenize(self.GetNextLine, self._ProcessToken)
+ except tokenize.TokenError:
+ pass # Ignore - will cause all subsequent text to be commented.
+ numAtEnd = len(self.GetText()) - self.lastPos
+ if numAtEnd:
+ self.attrs.append((axdebug.SOURCETEXT_ATTR_COMMENT, numAtEnd))
+ return self.attrs
+
+ # We also provide and manage DebugDocumentContext objects
+ def _MakeDebugCodeContext(self, lineNo, charPos, len):
+ return _wrap(contexts.DebugCodeContext(lineNo, charPos, len, self, self.site), axdebug.IID_IDebugCodeContext)
+ # Make a context at the given position. It should take up the entire context.
+ def _MakeContextAtPosition(self, charPos):
+ lineNo, offset = self.GetLineOfPosition(charPos)
+ try:
+ endPos = self.GetPositionOfLine(lineNo+1)
+ except:
+ endPos = charPos
+ codecontext = self._MakeDebugCodeContext(lineNo, charPos, endPos-charPos)
+ return codecontext
+
+ # Returns a DebugCodeContext. debugDocument can be None for smart hosts.
+ def GetCodeContextAtPosition(self, charPos):
+# trace("GetContextOfPos", charPos, maxChars)
+ # Convert to line number.
+ lineNo, offset = self.GetLineOfPosition(charPos)
+ charPos = self.GetPositionOfLine(lineNo)
+ try:
+ cc = self.codeContexts[charPos]
+# trace(" GetContextOfPos using existing")
+ except KeyError:
+ cc = self._MakeContextAtPosition(charPos)
+ self.codeContexts[charPos] = cc
+ return cc
+
+class SourceModuleContainer(SourceCodeContainer):
+ def __init__(self, module):
+ self.module = module
+ if hasattr(module, '__file__'):
+ fname = self.module.__file__
+ # Check for .pyc or .pyo or even .pys!
+ if fname[-1] in ['O','o','C','c', 'S', 's']: fname = fname[:-1]
+ try:
+ fname = win32api.GetFullPathName(fname)
+ except win32api.error:
+ pass
+ else:
+ if module.__name__=='__main__' and len(sys.argv)>0:
+ fname = sys.argv[0]
+ else:
+ fname = ""
+ SourceCodeContainer.__init__(self, None, fname)
+
+ def GetText(self):
+ if self.text is None:
+ fname = self.GetFileName()
+ if fname:
+ try:
+ self.text = open(fname, "r").read()
+ except IOError as details:
+ self.text = "# Exception opening file\n# %s" % (repr(details))
+ else:
+ self.text = "# No file available for module '%s'" % (self.module)
+ self._buildlines()
+ return self.text
+
+ def GetName(self, dnt):
+ name = self.module.__name__
+ try:
+ fname = win32api.GetFullPathName(self.module.__file__)
+ except win32api.error:
+ fname = self.module.__file__
+ except AttributeError:
+ fname = name
+ if dnt==axdebug.DOCUMENTNAMETYPE_APPNODE:
+ return name.split(".")[-1]
+ elif dnt==axdebug.DOCUMENTNAMETYPE_TITLE:
+ return fname
+ elif dnt==axdebug.DOCUMENTNAMETYPE_FILE_TAIL:
+ return os.path.split(fname)[1]
+ elif dnt==axdebug.DOCUMENTNAMETYPE_URL:
+ return "file:%s" % fname
+ else:
+ raise Exception(scode=winerror.E_UNEXPECTED)
+
+if __name__=='__main__':
+ import sys
+ sys.path.append(".")
+ import ttest
+ sc = SourceModuleContainer(ttest)
+# sc = SourceCodeContainer(open(sys.argv[1], "rb").read(), sys.argv[1])
+ attrs = sc.GetSyntaxColorAttributes()
+ attrlen = 0
+ for attr in attrs:
+ if type(attr)==type(()):
+ attrlen = attrlen + attr[1]
+ else:
+ attrlen = attrlen + 1
+ text = sc.GetText()
+ if attrlen!=len(text):
+ print("Lengths dont match!!! (%d/%d)" % (attrlen, len(text)))
+
+# print "Attributes:"
+# print attrs
+ print("GetLineOfPos=", sc.GetLineOfPosition(0))
+ print("GetLineOfPos=", sc.GetLineOfPosition(4))
+ print("GetLineOfPos=", sc.GetLineOfPosition(10))
diff --git a/Lib/site-packages/win32comext/axdebug/contexts.py b/Lib/site-packages/win32comext/axdebug/contexts.py
new file mode 100644
index 0000000..99fca26
--- /dev/null
+++ b/Lib/site-packages/win32comext/axdebug/contexts.py
@@ -0,0 +1,56 @@
+""" A module for managing the AXDebug I*Contexts
+
+"""
+import gateways, axdebug
+import pythoncom, win32com.server.util
+
+# Utility function for wrapping object created by this module.
+from .util import _wrap, _wrap_remove, trace
+from . import adb
+
+class DebugCodeContext(gateways.DebugCodeContext, gateways.DebugDocumentContext):
+ # NOTE: We also implement the IDebugDocumentContext interface for Simple Hosts.
+ # Thus, debugDocument may be NULL when we have smart hosts - but in that case, we
+ # wont be called upon to provide it.
+ _public_methods_ = gateways.DebugCodeContext._public_methods_ + \
+ gateways.DebugDocumentContext._public_methods_
+ _com_interfaces_ = gateways.DebugCodeContext._com_interfaces_ + \
+ gateways.DebugDocumentContext._com_interfaces_
+
+ def __init__(self, lineNo, charPos, len, codeContainer, debugSite):
+ self.debugSite = debugSite
+ self.offset = charPos
+ self.length = len
+ self.breakPointState = 0
+ self.lineno = lineNo
+ gateways.DebugCodeContext.__init__(self)
+ self.codeContainer = codeContainer
+
+ def _Close(self):
+ self.debugSite = None
+
+ def GetDocumentContext(self):
+ if self.debugSite is not None:
+ # We have a smart host - let him give it to us.
+ return self.debugSite.GetDocumentContextFromPosition(
+ self.codeContainer.sourceContext,
+ self.offset,
+ self.length)
+ else:
+ # Simple host - Fine - Ill do it myself!
+ return _wrap(self, axdebug.IID_IDebugDocumentContext)
+
+ def SetBreakPoint(self, bps):
+ self.breakPointState = bps
+ adb.OnSetBreakPoint(self, bps, self.lineno)
+
+ # The DebugDocumentContext methods for simple hosts.
+ def GetDocument(self):
+ return self.codeContainer.debugDocument
+
+ def EnumCodeContexts(self):
+ return _wrap(EnumDebugCodeContexts([self]), axdebug.IID_IEnumDebugCodeContexts)
+
+class EnumDebugCodeContexts(gateways.EnumDebugCodeContexts):
+ def _wrap(self, obj):
+ return _wrap(obj, axdebug.IID_IDebugCodeContext)
diff --git a/Lib/site-packages/win32comext/axdebug/debugger.py b/Lib/site-packages/win32comext/axdebug/debugger.py
new file mode 100644
index 0000000..c17bae1
--- /dev/null
+++ b/Lib/site-packages/win32comext/axdebug/debugger.py
@@ -0,0 +1,203 @@
+import sys, traceback, string
+
+from win32com.axscript import axscript
+from win32com.axdebug import codecontainer, axdebug, gateways, documents, contexts, adb, expressions
+from win32com.axdebug.util import trace, _wrap, _wrap_remove
+
+import pythoncom
+import win32api, winerror
+import os
+
+currentDebugger = None
+
+class ModuleTreeNode:
+ """Helper class for building a module tree
+ """
+ def __init__(self, module):
+ modName = module.__name__
+ self.moduleName = modName
+ self.module = module
+ self.realNode = None
+ self.cont = codecontainer.SourceModuleContainer(module)
+ def __repr__(self):
+ return "" % (self.module)
+ def Attach(self, parentRealNode):
+ self.realNode.Attach(parentRealNode)
+
+ def Close(self):
+ self.module = None
+ self.cont = None
+ self.realNode = None
+
+def BuildModule(module, built_nodes, rootNode, create_node_fn, create_node_args ):
+ if module:
+ keep = module.__name__
+ keep = keep and (built_nodes.get(module) is None)
+ if keep and hasattr(module, '__file__'):
+ keep = string.lower(os.path.splitext(module.__file__)[1]) not in [".pyd", ".dll"]
+# keep = keep and module.__name__=='__main__'
+ if module and keep:
+# print "keeping", module.__name__
+ node = ModuleTreeNode(module)
+ built_nodes[module] = node
+ realNode = create_node_fn(*(node,)+create_node_args)
+ node.realNode = realNode
+
+ # Split into parent nodes.
+ parts = string.split(module.__name__, '.')
+ if parts[-1][:8]=='__init__': parts = parts[:-1]
+ parent = string.join(parts[:-1], '.')
+ parentNode = rootNode
+ if parent:
+ parentModule = sys.modules[parent]
+ BuildModule(parentModule, built_nodes, rootNode, create_node_fn, create_node_args)
+ if parentModule in built_nodes:
+ parentNode = built_nodes[parentModule].realNode
+ node.Attach(parentNode)
+
+def RefreshAllModules(builtItems, rootNode, create_node, create_node_args):
+ for module in list(sys.modules.values()):
+ BuildModule(module, builtItems, rootNode, create_node, create_node_args)
+
+# realNode = pdm.CreateDebugDocumentHelper(None) # DebugDocumentHelper node?
+# app.CreateApplicationNode() # doc provider node.
+
+class CodeContainerProvider(documents.CodeContainerProvider):
+ def __init__(self, axdebugger):
+ self.axdebugger = axdebugger
+ documents.CodeContainerProvider.__init__(self)
+ self.currentNumModules = len(sys.modules)
+ self.nodes = {}
+ self.axdebugger.RefreshAllModules(self.nodes, self)
+
+ def FromFileName(self, fname):
+### It appears we cant add modules during a debug session!
+# if self.currentNumModules != len(sys.modules):
+# self.axdebugger.RefreshAllModules(self.nodes, self)
+# self.currentNumModules = len(sys.modules)
+# for key in self.ccsAndNodes.keys():
+# print "File:", key
+ return documents.CodeContainerProvider.FromFileName(self, fname)
+
+ def Close(self):
+ documents.CodeContainerProvider.Close(self)
+ self.axdebugger = None
+ print("Closing %d nodes" % (len(self.nodes)))
+ for node in self.nodes.values():
+ node.Close()
+ self.nodes = {}
+
+class OriginalInterfaceMaker:
+ def MakeInterfaces(self, pdm):
+ app = self.pdm.CreateApplication()
+ self.cookie = pdm.AddApplication(app)
+ root = app.GetRootNode()
+ return app, root
+
+ def CloseInterfaces(self, pdm):
+ pdm.RemoveApplication(self.cookie)
+
+class SimpleHostStyleInterfaceMaker:
+ def MakeInterfaces(self, pdm):
+ app = pdm.GetDefaultApplication()
+ root = app.GetRootNode()
+ return app, root
+
+ def CloseInterfaces(self, pdm):
+ pass
+
+
+class AXDebugger:
+ def __init__(self, interfaceMaker = None, processName = None):
+ if processName is None: processName = "Python Process"
+ if interfaceMaker is None: interfaceMaker = SimpleHostStyleInterfaceMaker()
+
+ self.pydebugger = adb.Debugger()
+
+ self.pdm=pythoncom.CoCreateInstance(axdebug.CLSID_ProcessDebugManager,None,pythoncom.CLSCTX_ALL, axdebug.IID_IProcessDebugManager)
+
+ self.app, self.root = interfaceMaker.MakeInterfaces(self.pdm)
+ self.app.SetName(processName)
+ self.interfaceMaker = interfaceMaker
+
+ expressionProvider = _wrap(expressions.ProvideExpressionContexts(), axdebug.IID_IProvideExpressionContexts)
+ self.expressionCookie = self.app.AddGlobalExpressionContextProvider(expressionProvider)
+
+ contProvider = CodeContainerProvider(self)
+ self.pydebugger.AttachApp(self.app, contProvider)
+
+ def Break(self):
+ # Get the frame we start debugging from - this is the frame 1 level up
+ try:
+ 1 + ''
+ except:
+ frame = sys.exc_info()[2].tb_frame.f_back
+
+ # Get/create the debugger, and tell it to break.
+ self.app.StartDebugSession()
+# self.app.CauseBreak()
+
+ self.pydebugger.SetupAXDebugging(None, frame)
+ self.pydebugger.set_trace()
+
+ def Close(self):
+ self.pydebugger.ResetAXDebugging()
+ self.interfaceMaker.CloseInterfaces(self.pdm)
+ self.pydebugger.CloseApp()
+ self.app.RemoveGlobalExpressionContextProvider(self.expressionCookie)
+ self.expressionCookie = None
+
+ self.pdm = None
+ self.app = None
+ self.pydebugger = None
+ self.root = None
+
+ def RefreshAllModules(self, nodes, containerProvider):
+ RefreshAllModules(nodes, self.root, self.CreateApplicationNode, (containerProvider,))
+
+ def CreateApplicationNode(self, node, containerProvider):
+ realNode = self.app.CreateApplicationNode()
+
+ document = documents.DebugDocumentText(node.cont)
+ document = _wrap(document, axdebug.IID_IDebugDocument)
+
+ node.cont.debugDocument = document
+
+ provider = documents.DebugDocumentProvider(document)
+ provider = _wrap(provider, axdebug.IID_IDebugDocumentProvider)
+ realNode.SetDocumentProvider(provider)
+
+ containerProvider.AddCodeContainer(node.cont, realNode)
+ return realNode
+
+def _GetCurrentDebugger():
+ global currentDebugger
+ if currentDebugger is None:
+ currentDebugger = AXDebugger()
+ return currentDebugger
+
+def Break():
+ _GetCurrentDebugger().Break()
+
+brk = Break
+set_trace = Break
+
+def dosomethingelse():
+ a=2
+ b = "Hi there"
+
+def dosomething():
+ a=1
+ b=2
+ dosomethingelse()
+
+def test():
+ Break()
+ input("Waiting...")
+ dosomething()
+ print("Done")
+
+if __name__=='__main__':
+ print("About to test the debugging interfaces!")
+ test()
+ print(" %d/%d com objects still alive" % (pythoncom._GetInterfaceCount(), pythoncom._GetGatewayCount()))
diff --git a/Lib/site-packages/win32comext/axdebug/documents.py b/Lib/site-packages/win32comext/axdebug/documents.py
new file mode 100644
index 0000000..5436d58
--- /dev/null
+++ b/Lib/site-packages/win32comext/axdebug/documents.py
@@ -0,0 +1,124 @@
+""" Management of documents for AXDebugging.
+"""
+
+import axdebug, gateways
+import pythoncom
+from .util import _wrap, _wrap_remove, RaiseNotImpl, trace
+from win32com.server.util import unwrap
+from . import codecontainer
+from . import contexts
+from win32com.server.exception import Exception
+import win32api, winerror, os, string, sys
+
+#def trace(*args):
+# pass
+
+def GetGoodFileName(fname):
+ if fname[0] != "<":
+ return win32api.GetFullPathName(fname)
+ return fname
+
+class DebugDocumentProvider(gateways.DebugDocumentProvider):
+ def __init__(self, doc):
+ self.doc = doc
+
+ def GetName(self, dnt):
+ return self.doc.GetName(dnt)
+
+ def GetDocumentClassId(self):
+ return self.doc.GetDocumentClassId()
+
+ def GetDocument(self):
+ return self.doc
+
+class DebugDocumentText(gateways.DebugDocumentInfo, gateways.DebugDocumentText, gateways.DebugDocument):
+ _com_interfaces_ = gateways.DebugDocumentInfo._com_interfaces_ + \
+ gateways.DebugDocumentText._com_interfaces_ + \
+ gateways.DebugDocument._com_interfaces_
+ _public_methods_ = gateways.DebugDocumentInfo._public_methods_ + \
+ gateways.DebugDocumentText._public_methods_ + \
+ gateways.DebugDocument._public_methods_
+ # A class which implements a DebugDocumentText, using the functionality
+ # provided by a codeContainer
+ def __init__(self, codeContainer):
+ gateways.DebugDocumentText.__init__(self)
+ gateways.DebugDocumentInfo.__init__(self)
+ gateways.DebugDocument.__init__(self)
+ self.codeContainer = codeContainer
+
+ def _Close(self):
+ self.docContexts = None
+# self.codeContainer._Close()
+ self.codeContainer = None
+ # IDebugDocumentInfo
+ def GetName(self, dnt):
+ return self.codeContainer.GetName(dnt)
+
+ def GetDocumentClassId(self):
+ return "{DF630910-1C1D-11d0-AE36-8C0F5E000000}"
+
+ # IDebugDocument has no methods!
+ #
+
+ # IDebugDocumentText methods.
+ # def GetDocumentAttributes
+ def GetSize(self):
+# trace("GetSize")
+ return self.codeContainer.GetNumLines(), self.codeContainer.GetNumChars()
+ def GetPositionOfLine(self, cLineNumber):
+ return self.codeContainer.GetPositionOfLine(cLineNumber)
+ def GetLineOfPosition(self, charPos):
+ return self.codeContainer.GetLineOfPosition(charPos)
+ def GetText(self, charPos, maxChars, wantAttr):
+ # Get all the attributes, else the tokenizer will get upset.
+ # XXX - not yet!
+# trace("GetText", charPos, maxChars, wantAttr)
+ cont = self.codeContainer
+ attr = cont.GetSyntaxColorAttributes()
+ return cont.GetText(), attr
+
+ def GetPositionOfContext(self, context):
+ trace("GetPositionOfContext", context)
+ context = unwrap(context)
+ return context.offset, context.length
+
+ # Return a DebugDocumentContext.
+ def GetContextOfPosition(self, charPos, maxChars):
+ # Make one
+ doc = _wrap(self, axdebug.IID_IDebugDocument)
+ rc = self.codeContainer.GetCodeContextAtPosition(charPos)
+ return rc.QueryInterface(axdebug.IID_IDebugDocumentContext)
+
+class CodeContainerProvider:
+ """An abstract Python class which provides code containers!
+
+ Given a Python file name (as the debugger knows it by) this will
+ return a CodeContainer interface suitable for use.
+
+ This provides a simple base imlpementation that simply supports
+ a dictionary of nodes and providers.
+ """
+ def __init__(self):
+ self.ccsAndNodes = {}
+
+ def AddCodeContainer(self, cc, node = None):
+ fname = GetGoodFileName(cc.fileName)
+ self.ccsAndNodes[fname] = cc, node
+
+ def FromFileName(self, fname):
+ cc, node = self.ccsAndNodes.get(GetGoodFileName(fname), (None, None))
+# if cc is None:
+# print "FromFileName for %s returning None" % fname
+ return cc
+
+ def Close(self):
+ for cc, node in self.ccsAndNodes.values():
+ try:
+ # Must close the node before closing the provider
+ # as node may make calls on provider (eg Reset breakpoints etc)
+ if node is not None:
+ node.Close()
+ cc._Close()
+ except pythoncom.com_error:
+ pass
+ self.ccsAndNodes = {}
diff --git a/Lib/site-packages/win32comext/axdebug/dump.py b/Lib/site-packages/win32comext/axdebug/dump.py
new file mode 100644
index 0000000..c23dcff
--- /dev/null
+++ b/Lib/site-packages/win32comext/axdebug/dump.py
@@ -0,0 +1,48 @@
+import sys, string
+import traceback
+from win32com.axdebug import axdebug
+from win32com.client.util import Enumerator
+import pythoncom
+
+def DumpDebugApplicationNode(node, level = 0):
+ # Recursive dump of a DebugApplicationNode
+ spacer = " " * level
+ for desc, attr in [("Node Name", axdebug.DOCUMENTNAMETYPE_APPNODE),
+ ("Title", axdebug.DOCUMENTNAMETYPE_TITLE),
+ ("Filename", axdebug.DOCUMENTNAMETYPE_FILE_TAIL),
+ ("URL", axdebug.DOCUMENTNAMETYPE_URL),
+ ]:
+ try:
+ info = node.GetName(attr)
+ except pythoncom.com_error:
+ info = ""
+ print("%s%s: %s" % (spacer, desc, info))
+ try:
+ doc = node.GetDocument()
+ except pythoncom.com_error:
+ doc = None
+ if doc:
+ doctext = doc.QueryInterface(axdebug.IID_IDebugDocumentText)
+ numLines, numChars = doctext.GetSize()
+# text, attr = doctext.GetText(0, 20, 1)
+ text, attr = doctext.GetText(0, numChars, 1)
+ print("%sText is %s, %d bytes long" % (spacer, repr(text[:40]+"..."), len(text)))
+ else:
+ print("%s%s" % (spacer, ""))
+
+ for child in Enumerator(node.EnumChildren()):
+ DumpDebugApplicationNode(child, level+1)
+
+def dumpall():
+ dm=pythoncom.CoCreateInstance(axdebug.CLSID_MachineDebugManager,None,pythoncom.CLSCTX_ALL, axdebug.IID_IMachineDebugManager)
+ e=Enumerator(dm.EnumApplications())
+ for app in e:
+ print("Application: %s" % app.GetName())
+ node = app.GetRootNode() # of type PyIDebugApplicationNode->PyIDebugDocumentProvider->PyIDebugDocumentInfo
+ DumpDebugApplicationNode(node)
+
+if __name__=='__main__':
+ try:
+ dumpall()
+ except:
+ traceback.print_exc()
diff --git a/Lib/site-packages/win32comext/axdebug/expressions.py b/Lib/site-packages/win32comext/axdebug/expressions.py
new file mode 100644
index 0000000..ae4992c
--- /dev/null
+++ b/Lib/site-packages/win32comext/axdebug/expressions.py
@@ -0,0 +1,156 @@
+import axdebug, gateways
+from .util import _wrap, _wrap_remove, RaiseNotImpl
+import io, traceback
+from pprint import pprint
+from win32com.server.exception import COMException
+import winerror
+import string
+import sys
+
+# Given an object, return a nice string
+def MakeNiceString(ob):
+ stream = io.StringIO()
+ pprint(ob, stream)
+ return string.strip(stream.getvalue())
+
+class ProvideExpressionContexts(gateways.ProvideExpressionContexts):
+ pass
+
+class ExpressionContext(gateways.DebugExpressionContext):
+ def __init__(self, frame):
+ self.frame = frame
+ def ParseLanguageText(self, code, radix, delim, flags):
+ return _wrap(Expression(self.frame, code, radix, delim, flags), axdebug.IID_IDebugExpression)
+ def GetLanguageInfo(self):
+# print "GetLanguageInfo"
+ return "Python", "{DF630910-1C1D-11d0-AE36-8C0F5E000000}"
+
+class Expression(gateways.DebugExpression):
+ def __init__(self, frame, code, radix, delim, flags):
+ self.callback = None
+ self.frame = frame
+ self.code = code
+ self.radix = radix
+ self.delim = delim
+ self.flags = flags
+ self.isComplete = 0
+ self.result=None
+ self.hresult = winerror.E_UNEXPECTED
+ def Start(self, callback):
+ try:
+ try:
+ try:
+ self.result = eval(self.code, self.frame.f_globals, self.frame.f_locals)
+ except SyntaxError:
+ exec(self.code, self.frame.f_globals, self.frame.f_locals)
+ self.result = ""
+ self.hresult = 0
+ except:
+ l = traceback.format_exception_only(sys.exc_info()[0], sys.exc_info()[1])
+ # l is a list of strings with trailing "\n"
+ self.result = string.join(map(lambda s:s[:-1], l), "\n")
+ self.hresult = winerror.E_FAIL
+ finally:
+ self.isComplete = 1
+ callback.onComplete()
+ def Abort(self):
+ print("** ABORT **")
+
+ def QueryIsComplete(self):
+ return self.isComplete
+
+ def GetResultAsString(self):
+# print "GetStrAsResult returning", self.result
+ return self.hresult, MakeNiceString(self.result)
+
+ def GetResultAsDebugProperty(self):
+ result = _wrap(DebugProperty(self.code, self.result, None, self.hresult), axdebug.IID_IDebugProperty)
+ return self.hresult, result
+
+def MakeEnumDebugProperty(object, dwFieldSpec, nRadix, iid, stackFrame = None):
+ name_vals = []
+ if hasattr(object, "items") and hasattr(object, "keys"): # If it is a dict.
+ name_vals = iter(object.items())
+ dictionary = object
+ elif hasattr(object, "__dict__"): #object with dictionary, module
+ name_vals = iter(object.__dict__.items())
+ dictionary = object.__dict__
+ infos = []
+ for name, val in name_vals:
+ infos.append(GetPropertyInfo(name, val, dwFieldSpec, nRadix, 0, dictionary, stackFrame))
+ return _wrap(EnumDebugPropertyInfo(infos), axdebug.IID_IEnumDebugPropertyInfo)
+
+def GetPropertyInfo(obname, obvalue, dwFieldSpec, nRadix, hresult=0, dictionary = None, stackFrame = None):
+ # returns a tuple
+ name = typ = value = fullname = attrib = dbgprop = None
+ if dwFieldSpec & axdebug.DBGPROP_INFO_VALUE:
+ value = MakeNiceString(obvalue)
+ if dwFieldSpec & axdebug.DBGPROP_INFO_NAME:
+ name = obname
+ if dwFieldSpec & axdebug.DBGPROP_INFO_TYPE:
+ if hresult:
+ typ = "Error"
+ else:
+ try:
+ typ = type(obvalue).__name__
+ except AttributeError:
+ typ = str(type(obvalue))
+ if dwFieldSpec & axdebug.DBGPROP_INFO_FULLNAME:
+ fullname = obname
+ if dwFieldSpec & axdebug.DBGPROP_INFO_ATTRIBUTES:
+ if hasattr(obvalue, "has_key") or hasattr(obvalue, "__dict__"): # If it is a dict or object
+ attrib = axdebug.DBGPROP_ATTRIB_VALUE_IS_EXPANDABLE
+ else:
+ attrib = 0
+ if dwFieldSpec & axdebug.DBGPROP_INFO_DEBUGPROP:
+ dbgprop = _wrap(DebugProperty(name, obvalue, None, hresult, dictionary, stackFrame), axdebug.IID_IDebugProperty)
+ return name, typ, value, fullname, attrib, dbgprop
+
+from win32com.server.util import ListEnumeratorGateway
+class EnumDebugPropertyInfo(ListEnumeratorGateway):
+ """A class to expose a Python sequence as an EnumDebugCodeContexts
+
+ Create an instance of this class passing a sequence (list, tuple, or
+ any sequence protocol supporting object) and it will automatically
+ support the EnumDebugCodeContexts interface for the object.
+
+ """
+ _public_methods_ = ListEnumeratorGateway._public_methods_ + ["GetCount"]
+ _com_interfaces_ = [ axdebug.IID_IEnumDebugPropertyInfo]
+ def GetCount(self):
+ return len(self._list_)
+ def _wrap(self, ob):
+ return ob
+
+class DebugProperty:
+ _com_interfaces_ = [axdebug.IID_IDebugProperty]
+ _public_methods_ = ['GetPropertyInfo', 'GetExtendedInfo', 'SetValueAsString',
+ 'EnumMembers', 'GetParent'
+ ]
+ def __init__(self, name, value, parent = None, hresult = 0, dictionary = None, stackFrame = None):
+ self.name = name
+ self.value = value
+ self.parent = parent
+ self.hresult = hresult
+ self.dictionary = dictionary
+ self.stackFrame = stackFrame
+
+ def GetPropertyInfo(self, dwFieldSpec, nRadix):
+ return GetPropertyInfo(self.name, self.value, dwFieldSpec, nRadix, self.hresult, dictionary, stackFrame)
+
+ def GetExtendedInfo(self): ### Note - not in the framework.
+ RaiseNotImpl("DebugProperty::GetExtendedInfo")
+
+ def SetValueAsString(self, value, radix):
+ if self.stackFrame and self.dictionary:
+ self.dictionary[self.name]= eval(value,self.stackFrame.f_globals, self.stackFrame.f_locals)
+ else:
+ RaiseNotImpl("DebugProperty::SetValueAsString")
+
+ def EnumMembers(self, dwFieldSpec, nRadix, iid):
+ # Returns IEnumDebugPropertyInfo
+ return MakeEnumDebugProperty(self.value, dwFieldSpec, nRadix, iid, self.stackFrame)
+
+ def GetParent(self):
+ # return IDebugProperty
+ RaiseNotImpl("DebugProperty::GetParent")
diff --git a/Lib/site-packages/win32comext/axdebug/gateways.py b/Lib/site-packages/win32comext/axdebug/gateways.py
new file mode 100644
index 0000000..9957c8e
--- /dev/null
+++ b/Lib/site-packages/win32comext/axdebug/gateways.py
@@ -0,0 +1,452 @@
+# Classes which describe interfaces.
+
+from win32com.server.exception import Exception
+from win32com.server.util import ListEnumeratorGateway
+from win32com.axdebug import axdebug
+from win32com.axdebug.util import RaiseNotImpl, _wrap
+import pythoncom
+import win32com.server.connect
+import winerror
+
+class EnumDebugCodeContexts(ListEnumeratorGateway):
+ """A class to expose a Python sequence as an EnumDebugCodeContexts
+
+ Create an instance of this class passing a sequence (list, tuple, or
+ any sequence protocol supporting object) and it will automatically
+ support the EnumDebugCodeContexts interface for the object.
+
+ """
+ _com_interfaces_ = [ axdebug.IID_IEnumDebugCodeContexts ]
+
+class EnumDebugStackFrames(ListEnumeratorGateway):
+ """A class to expose a Python sequence as an EnumDebugStackFrames
+
+ Create an instance of this class passing a sequence (list, tuple, or
+ any sequence protocol supporting object) and it will automatically
+ support the EnumDebugStackFrames interface for the object.
+
+ """
+ _com_interfaces_ = [ axdebug.IID_IEnumDebugStackFrames ]
+
+class EnumDebugApplicationNodes(ListEnumeratorGateway):
+ """A class to expose a Python sequence as an EnumDebugStackFrames
+
+ Create an instance of this class passing a sequence (list, tuple, or
+ any sequence protocol supporting object) and it will automatically
+ support the EnumDebugApplicationNodes interface for the object.
+
+ """
+ _com_interfaces_ = [ axdebug.IID_IEnumDebugApplicationNodes ]
+
+class EnumRemoteDebugApplications(ListEnumeratorGateway):
+ _com_interfaces_ = [ axdebug.IID_IEnumRemoteDebugApplications ]
+
+class EnumRemoteDebugApplicationThreads(ListEnumeratorGateway):
+ _com_interfaces_ = [ axdebug.IID_IEnumRemoteDebugApplicationThreads ]
+
+class DebugDocumentInfo:
+ _public_methods_ = ["GetName", "GetDocumentClassId"]
+ _com_interfaces_ = [axdebug.IID_IDebugDocumentInfo]
+ def __init__(self):
+ pass
+ def GetName(self, dnt):
+ """ Get the one of the name of the document
+ dnt -- int DOCUMENTNAMETYPE
+ """
+ RaiseNotImpl("GetName")
+ def GetDocumentClassId(self):
+ """
+ Result must be an IID object (or string representing one).
+ """
+ RaiseNotImpl("GetDocumentClassId")
+
+class DebugDocumentProvider(DebugDocumentInfo):
+ _public_methods_ = DebugDocumentInfo._public_methods_ + ["GetDocument"]
+ _com_interfaces_ = DebugDocumentInfo._com_interfaces_ + [axdebug.IID_IDebugDocumentProvider]
+ def GetDocument(self):
+ RaiseNotImpl("GetDocument")
+
+class DebugApplicationNode(DebugDocumentProvider):
+ """Provides the functionality of IDebugDocumentProvider, plus a context within a project tree.
+ """
+ _public_methods_ = """EnumChildren GetParent SetDocumentProvider
+ Close Attach Detach""".split() + \
+ DebugDocumentProvider._public_methods_
+ _com_interfaces_ = [axdebug.IID_IDebugDocumentProvider] + \
+ DebugDocumentProvider._com_interfaces_
+ def __init__(self):
+ DebugDocumentProvider.__init__(self)
+ def EnumChildren(self):
+ # Result is type PyIEnumDebugApplicationNodes
+ RaiseNotImpl("EnumChildren")
+ def GetParent(self):
+ # result is type PyIDebugApplicationNode
+ RaiseNotImpl("GetParent")
+ def SetDocumentProvider(self, pddp): # PyIDebugDocumentProvider pddp
+ # void result.
+ RaiseNotImpl("SetDocumentProvider")
+ def Close(self):
+ # void result.
+ RaiseNotImpl("Close")
+ def Attach(self, parent): # PyIDebugApplicationNode
+ # void result.
+ RaiseNotImpl("Attach")
+ def Detach(self):
+ # void result.
+ RaiseNotImpl("Detach")
+
+class DebugApplicationNodeEvents:
+ """Event interface for DebugApplicationNode object.
+ """
+ _public_methods_ = "onAddChild onRemoveChild onDetach".split()
+ _com_interfaces_ = [axdebug.IID_IDebugApplicationNodeEvents]
+ def __init__(self):
+ pass
+ def onAddChild(self, child): # PyIDebugApplicationNode
+ # void result.
+ RaiseNotImpl("onAddChild")
+ def onRemoveChild(self, child): # PyIDebugApplicationNode
+ # void result.
+ RaiseNotImpl("onRemoveChild")
+ def onDetach(self):
+ # void result.
+ RaiseNotImpl("onDetach")
+ def onAttach(self, parent): # PyIDebugApplicationNode
+ # void result.
+ RaiseNotImpl("onAttach")
+
+class DebugDocument(DebugDocumentInfo):
+ """The base interface to all debug documents.
+ """
+ _public_methods_ = DebugDocumentInfo._public_methods_
+ _com_interfaces_ = [axdebug.IID_IDebugDocument] + DebugDocumentInfo._com_interfaces_
+
+class DebugDocumentText(DebugDocument):
+ """The interface to a text only debug document.
+ """
+ _com_interfaces_ = [axdebug.IID_IDebugDocumentText] + \
+ DebugDocument._com_interfaces_
+ _public_methods_ = ["GetDocumentAttributes", "GetSize",
+ "GetPositionOfLine", "GetLineOfPosition", "GetText",
+ "GetPositionOfContext", "GetContextOfPosition"] + \
+ DebugDocument._public_methods_
+ def __init__(self):
+ pass
+ # IDebugDocumentText
+ def GetDocumentAttributes(self):
+ # Result is int (TEXT_DOC_ATTR)
+ RaiseNotImpl("GetDocumentAttributes")
+ def GetSize(self):
+ # Result is (numLines, numChars)
+ RaiseNotImpl("GetSize")
+ def GetPositionOfLine(self, cLineNumber):
+ # Result is int char position
+ RaiseNotImpl("GetPositionOfLine")
+ def GetLineOfPosition(self, charPos):
+ # Result is int, int (lineNo, offset)
+ RaiseNotImpl("GetLineOfPosition")
+ def GetText(self, charPos, maxChars, wantAttr):
+ """Params
+ charPos -- integer
+ maxChars -- integer
+ wantAttr -- Should the function compute attributes.
+
+ Return value must be (string, attribtues). attributes may be
+ None if(not wantAttr)
+ """
+ RaiseNotImpl("GetText")
+ def GetPositionOfContext(self, debugDocumentContext):
+ """Params
+ debugDocumentContext -- a PyIDebugDocumentContext object.
+
+ Return value must be (charPos, numChars)
+ """
+ RaiseNotImpl("GetPositionOfContext")
+ def GetContextOfPosition(self, charPos, maxChars):
+ """Params are integers.
+ Return value must be PyIDebugDocumentContext object
+ """
+ print(self)
+ RaiseNotImpl("GetContextOfPosition")
+
+class DebugDocumentTextExternalAuthor:
+ """Allow external editors to edit file-based debugger documents, and to notify the document when the source file has been changed.
+ """
+ _public_methods_ = ["GetPathName", "GetFileName", "NotifyChanged"]
+ _com_interfaces_ = [axdebug.IID_IDebugDocumentTextExternalAuthor]
+ def __init__(self):
+ pass
+ def GetPathName(self):
+ """Return the full path (including file name) to the document's source file.
+
+ Result must be (filename, fIsOriginal), where
+ - if fIsOriginalPath is TRUE if the path refers to the original file for the document.
+ - if fIsOriginalPath is FALSE if the path refers to a newly created temporary file.
+
+ raise Exception(winerror.E_FAIL) if no source file can be created/determined.
+ """
+ RaiseNotImpl("GetPathName")
+
+ def GetFileName(self):
+ """Return just the name of the document, with no path information. (Used for "Save As...")
+
+ Result is a string
+ """
+ RaiseNotImpl("GetFileName")
+
+ def NotifyChanged(self):
+ """ Notify the host that the document's source file has been saved and
+ that its contents should be refreshed.
+ """
+ RaiseNotImpl("NotifyChanged")
+
+
+class DebugDocumentTextEvents:
+ _public_methods_ = """onDestroy onInsertText onRemoveText
+ onReplaceText onUpdateTextAttributes
+ onUpdateDocumentAttributes""".split()
+ _com_interfaces_ = [ axdebug.IID_IDebugDocumentTextEvents ]
+ def __init__(self):
+ pass
+ def onDestroy(self):
+ # Result is void.
+ RaiseNotImpl("onDestroy")
+ def onInsertText(self, cCharacterPosition, cNumToInsert):
+ # Result is void.
+ RaiseNotImpl("onInsertText")
+ def onRemoveText(self, cCharacterPosition, cNumToRemove):
+ # Result is void.
+ RaiseNotImpl("onRemoveText")
+ def onReplaceText(self, cCharacterPosition, cNumToReplace):
+ # Result is void.
+ RaiseNotImpl("onReplaceText")
+ def onUpdateTextAttributes(self, cCharacterPosition, cNumToUpdate):
+ # Result is void.
+ RaiseNotImpl("onUpdateTextAttributes")
+ def onUpdateDocumentAttributes(self,textdocattr): # TEXT_DOC_ATTR
+ # Result is void.
+ RaiseNotImpl("onUpdateDocumentAttributes")
+
+class DebugDocumentContext:
+ _public_methods_ = [ 'GetDocument', 'EnumCodeContexts']
+ _com_interfaces_ = [ axdebug.IID_IDebugDocumentContext ]
+ def __init__(self):
+ pass
+ def GetDocument(self):
+ """Return value must be a PyIDebugDocument object
+ """
+ RaiseNotImpl("GetDocument")
+
+ def EnumCodeContexts(self):
+ """Return value must be a PyIEnumDebugCodeContexts object
+ """
+ RaiseNotImpl("EnumCodeContexts")
+
+
+class DebugCodeContext:
+ _public_methods_ = [ 'GetDocumentContext', 'SetBreakPoint']
+ _com_interfaces_ = [ axdebug.IID_IDebugCodeContext ]
+ def __init__(self):
+ pass
+ def GetDocumentContext(self):
+ """Return value must be a PyIDebugDocumentContext object
+ """
+ RaiseNotImpl("GetDocumentContext")
+ def SetBreakPoint(self, bps):
+ """bps -- an integer with flags.
+ """
+ RaiseNotImpl("SetBreakPoint")
+
+
+class DebugStackFrame:
+ """Abstraction representing a logical stack frame on the stack of a thread."""
+ _public_methods_ = [ 'GetCodeContext', 'GetDescriptionString', 'GetLanguageString', 'GetThread', 'GetDebugProperty']
+ _com_interfaces_ = [ axdebug.IID_IDebugStackFrame ]
+ def __init__(self):
+ pass
+ def GetCodeContext(self):
+ """Returns the current code context associated with the stack frame.
+
+ Return value must be a IDebugCodeContext object
+ """
+ RaiseNotImpl("GetCodeContext")
+ def GetDescriptionString(self, fLong):
+ """Returns a textual description of the stack frame.
+
+ fLong -- A flag indicating if the long name is requested.
+ """
+ RaiseNotImpl("GetDescriptionString")
+ def GetLanguageString(self):
+ """Returns a short or long textual description of the language.
+
+ fLong -- A flag indicating if the long name is requested.
+ """
+ RaiseNotImpl("GetLanguageString")
+ def GetThread(self):
+ """ Returns the thread associated with this stack frame.
+
+ Result must be a IDebugApplicationThread
+ """
+ RaiseNotImpl("GetThread")
+ def GetDebugProperty(self):
+ RaiseNotImpl("GetDebugProperty")
+
+
+class DebugDocumentHost:
+ """The interface from the IDebugDocumentHelper back to
+ the smart host or language engine. This interface
+ exposes host specific functionality such as syntax coloring.
+ """
+ _public_methods_ = [ 'GetDeferredText', 'GetScriptTextAttributes', 'OnCreateDocumentContext', 'GetPathName', 'GetFileName', 'NotifyChanged']
+ _com_interfaces_ = [ axdebug.IID_IDebugDocumentHost ]
+ def __init__(self):
+ pass
+ def GetDeferredText(self, dwTextStartCookie, maxChars, bWantAttr):
+ RaiseNotImpl("GetDeferredText")
+
+ def GetScriptTextAttributes(self, codeText, delimterText, flags):
+ # Result must be an attribute sequence of same "length" as the code.
+ RaiseNotImpl("GetScriptTextAttributes")
+
+ def OnCreateDocumentContext(self):
+ # Result must be a PyIUnknown
+ RaiseNotImpl("OnCreateDocumentContext")
+
+ def GetPathName(self):
+ # Result must be (string, int) where the int is a BOOL
+ # - TRUE if the path refers to the original file for the document.
+ # - FALSE if the path refers to a newly created temporary file.
+ # - raise Exception(scode=E_FAIL) if no source file can be created/determined.
+ RaiseNotImpl("GetPathName")
+
+ def GetFileName(self):
+ # Result is a string with just the name of the document, no path information.
+ RaiseNotImpl("GetFileName")
+
+ def NotifyChanged(self):
+ RaiseNotImpl("NotifyChanged")
+
+# Additional gateway related functions.
+
+class DebugDocumentTextConnectServer:
+ _public_methods_ = win32com.server.connect.IConnectionPointContainer_methods + win32com.server.connect.IConnectionPoint_methods
+ _com_interfaces_ = [pythoncom.IID_IConnectionPoint, pythoncom.IID_IConnectionPointContainer]
+ # IConnectionPoint interfaces
+ def __init__(self):
+ self.cookieNo = -1
+ self.connections = {}
+ def EnumConnections(self):
+ RaiseNotImpl("EnumConnections")
+ def GetConnectionInterface(self):
+ RaiseNotImpl("GetConnectionInterface")
+ def GetConnectionPointContainer(self):
+ return _wrap(self)
+ def Advise(self, pUnk):
+ # Creates a connection to the client. Simply allocate a new cookie,
+ # find the clients interface, and store it in a dictionary.
+ interface = pUnk.QueryInterface(axdebug.IID_IDebugDocumentTextEvents,1)
+ self.cookieNo = self.cookieNo + 1
+ self.connections[self.cookieNo] = interface
+ return self.cookieNo
+ def Unadvise(self, cookie):
+ # Destroy a connection - simply delete interface from the map.
+ try:
+ del self.connections[cookie]
+ except KeyError:
+ return Exception(scode=winerror.E_UNEXPECTED)
+ # IConnectionPointContainer interfaces
+ def EnumConnectionPoints(self):
+ RaiseNotImpl("EnumConnectionPoints")
+ def FindConnectionPoint(self, iid):
+ # Find a connection we support. Only support the single event interface.
+ if iid==axdebug.IID_IDebugDocumentTextEvents:
+ return _wrap(self)
+ raise Exception(scode=winerror.E_NOINTERFACE) # ??
+
+class RemoteDebugApplicationEvents:
+ _public_methods_ = ["OnConnectDebugger","OnDisconnectDebugger","OnSetName","OnDebugOutput","OnClose","OnEnterBreakPoint","OnLeaveBreakPoint","OnCreateThread","OnDestroyThread","OnBreakFlagChange"]
+ _com_interfaces_ = [axdebug.IID_IRemoteDebugApplicationEvents]
+ def OnConnectDebugger(self, appDebugger):
+ """appDebugger -- a PyIApplicationDebugger
+ """
+ RaiseNotImpl("OnConnectDebugger")
+ def OnDisconnectDebugger(self):
+ RaiseNotImpl("OnDisconnectDebugger")
+ def OnSetName(self, name):
+ RaiseNotImpl("OnSetName")
+ def OnDebugOutput(self, string):
+ RaiseNotImpl("OnDebugOutput")
+ def OnClose(self):
+ RaiseNotImpl("OnClose")
+ def OnEnterBreakPoint(self, rdat):
+ """rdat -- PyIRemoteDebugApplicationThread
+ """
+ RaiseNotImpl("OnEnterBreakPoint")
+ def OnLeaveBreakPoint(self, rdat):
+ """rdat -- PyIRemoteDebugApplicationThread
+ """
+ RaiseNotImpl("OnLeaveBreakPoint")
+ def OnCreateThread(self, rdat):
+ """rdat -- PyIRemoteDebugApplicationThread
+ """
+ RaiseNotImpl("OnCreateThread")
+ def OnDestroyThread(self, rdat):
+ """rdat -- PyIRemoteDebugApplicationThread
+ """
+ RaiseNotImpl("OnDestroyThread")
+ def OnBreakFlagChange(self, abf, rdat):
+ """abf -- int - one of the axdebug.APPBREAKFLAGS constants
+ rdat -- PyIRemoteDebugApplicationThread
+ RaiseNotImpl("OnBreakFlagChange")
+ """
+class DebugExpressionContext:
+ _public_methods_ = ["ParseLanguageText", "GetLanguageInfo"]
+ _com_interfaces_ = [axdebug.IID_IDebugExpressionContext]
+ def __init__(self):
+ pass
+ def ParseLanguageText(self, code, radix, delim, flags):
+ """
+ result is IDebugExpression
+ """
+ RaiseNotImpl("ParseLanguageText")
+ def GetLanguageInfo(self):
+ """
+ result is (string langName, iid langId)
+ """
+ RaiseNotImpl("GetLanguageInfo")
+
+class DebugExpression:
+ _public_methods_ = ["Start", "Abort", "QueryIsComplete", "GetResultAsString", "GetResultAsDebugProperty"]
+ _com_interfaces_ = [axdebug.IID_IDebugExpression]
+ def Start(self, callback):
+ """
+ callback -- an IDebugExpressionCallback
+
+ result - void
+ """
+ RaiseNotImpl("Start")
+ def Abort(self):
+ """
+ no params
+ result -- void
+ """
+ RaiseNotImpl("Abort")
+
+ def QueryIsComplete(self):
+ """
+ no params
+ result -- void
+ """
+ RaiseNotImpl("QueryIsComplete")
+
+ def GetResultAsString(self):
+ RaiseNotImpl("GetResultAsString")
+
+ def GetResultAsDebugProperty(self):
+ RaiseNotImpl("GetResultAsDebugProperty")
+
+class ProvideExpressionContexts:
+ _public_methods_ = ["EnumExpressionContexts"]
+ _com_interfaces_ = [axdebug.IID_IProvideExpressionContexts]
+ def EnumExpressionContexts(self):
+ RaiseNotImpl("EnumExpressionContexts")
diff --git a/Lib/site-packages/win32comext/axdebug/stackframe.py b/Lib/site-packages/win32comext/axdebug/stackframe.py
new file mode 100644
index 0000000..2247b74
--- /dev/null
+++ b/Lib/site-packages/win32comext/axdebug/stackframe.py
@@ -0,0 +1,146 @@
+"""Support for stack-frames.
+
+Provides Implements a nearly complete wrapper for a stack frame.
+"""
+import sys
+from .util import _wrap, RaiseNotImpl
+import expressions, gateways, axdebug, winerror
+import pythoncom
+from win32com.server.exception import COMException
+
+from .util import trace
+#def trace(*args):
+# pass
+
+class EnumDebugStackFrames(gateways.EnumDebugStackFrames):
+ """A class that given a debugger object, can return an enumerator
+ of DebugStackFrame objects.
+ """
+ def __init__(self, debugger):
+ infos = []
+ frame = debugger.currentframe
+# print "Stack check"
+ while frame:
+# print " Checking frame", frame.f_code.co_filename, frame.f_lineno-1, frame.f_trace,
+ # Get a DebugCodeContext for the stack frame. If we fail, then it
+ # is not debuggable, and therefore not worth displaying.
+ cc = debugger.codeContainerProvider.FromFileName(frame.f_code.co_filename)
+ if cc is not None:
+ try:
+ address = frame.f_locals['__axstack_address__']
+ except KeyError:
+# print "Couldnt find stack address for",frame.f_code.co_filename, frame.f_lineno-1
+ # Use this one, even tho it is wrong :-(
+ address = axdebug.GetStackAddress()
+ frameInfo = DebugStackFrame(frame, frame.f_lineno-1, cc), address, address+1, 0, None
+ infos.append(frameInfo)
+# print "- Kept!"
+# else:
+# print "- rejected"
+ frame = frame.f_back
+
+ gateways.EnumDebugStackFrames.__init__(self, infos, 0)
+# def __del__(self):
+# print "EnumDebugStackFrames dieing"
+
+ def Next(self, count):
+ return gateways.EnumDebugStackFrames.Next(self, count)
+
+# def _query_interface_(self, iid):
+# from win32com.util import IIDToInterfaceName
+# print "EnumDebugStackFrames QI with %s (%s)" % (IIDToInterfaceName(iid), str(iid))
+# return 0
+ def _wrap(self, obj):
+ # This enum returns a tuple, with 2 com objects in it.
+ obFrame, min, lim, fFinal, obFinal = obj
+ obFrame = _wrap(obFrame, axdebug.IID_IDebugStackFrame)
+ if obFinal:
+ obFinal = _wrap(obFinal, pythoncom.IID_IUnknown)
+ return obFrame, min, lim, fFinal, obFinal
+
+class DebugStackFrame(gateways.DebugStackFrame):
+ def __init__(self, frame, lineno, codeContainer):
+ self.frame = frame
+ self.lineno = lineno
+ self.codeContainer = codeContainer
+ self.expressionContext = None
+# def __del__(self):
+# print "DSF dieing"
+ def _query_interface_(self, iid):
+ if iid==axdebug.IID_IDebugExpressionContext:
+ if self.expressionContext is None:
+ self.expressionContext = _wrap(expressions.ExpressionContext(self.frame), axdebug.IID_IDebugExpressionContext)
+ return self.expressionContext
+# from win32com.util import IIDToInterfaceName
+# print "DebugStackFrame QI with %s (%s)" % (IIDToInterfaceName(iid), str(iid))
+ return 0
+ #
+ # The following need implementation
+ def GetThread(self):
+ """ Returns the thread associated with this stack frame.
+
+ Result must be a IDebugApplicationThread
+ """
+ RaiseNotImpl("GetThread")
+
+ def GetCodeContext(self):
+ offset = self.codeContainer.GetPositionOfLine(self.lineno)
+ return self.codeContainer.GetCodeContextAtPosition(offset)
+ #
+ # The following are usefully implemented
+ def GetDescriptionString(self, fLong):
+ filename = self.frame.f_code.co_filename
+ s = ""
+ if 0: #fLong:
+ s = s + filename
+ if self.frame.f_code.co_name:
+ s = s + self.frame.f_code.co_name
+ else:
+ s = s + ""
+ return s
+ def GetLanguageString(self, fLong):
+ if fLong:
+ return "Python ActiveX Scripting Engine"
+ else:
+ return "Python"
+ def GetDebugProperty(self):
+ return _wrap(StackFrameDebugProperty(self.frame), axdebug.IID_IDebugProperty)
+
+class DebugStackFrameSniffer:
+ _public_methods_ = ["EnumStackFrames"]
+ _com_interfaces_ = [axdebug.IID_IDebugStackFrameSniffer]
+ def __init__(self, debugger):
+ self.debugger = debugger
+ trace("DebugStackFrameSniffer instantiated")
+# def __del__(self):
+# print "DSFS dieing"
+ def EnumStackFrames(self):
+ trace("DebugStackFrameSniffer.EnumStackFrames called")
+ return _wrap(EnumDebugStackFrames(self.debugger), axdebug.IID_IEnumDebugStackFrames)
+
+# A DebugProperty for a stack frame.
+class StackFrameDebugProperty:
+ _com_interfaces_ = [axdebug.IID_IDebugProperty]
+ _public_methods_ = ['GetPropertyInfo', 'GetExtendedInfo', 'SetValueAsString',
+ 'EnumMembers', 'GetParent'
+ ]
+ def __init__(self, frame):
+ self.frame = frame
+
+ def GetPropertyInfo(self, dwFieldSpec, nRadix):
+ RaiseNotImpl("StackFrameDebugProperty::GetPropertyInfo")
+ def GetExtendedInfo(self): ### Note - not in the framework.
+ RaiseNotImpl("StackFrameDebugProperty::GetExtendedInfo")
+
+ def SetValueAsString(self, value, radix):
+ #
+ RaiseNotImpl("DebugProperty::SetValueAsString")
+
+ def EnumMembers(self, dwFieldSpec, nRadix, iid):
+ print("EnumMembers", dwFieldSpec, nRadix, iid)
+ from . import expressions
+ return expressions.MakeEnumDebugProperty(self.frame.f_locals, dwFieldSpec, nRadix, iid, self.frame)
+
+ def GetParent(self):
+ # return IDebugProperty
+ RaiseNotImpl("DebugProperty::GetParent")
diff --git a/Lib/site-packages/win32comext/axdebug/util.py b/Lib/site-packages/win32comext/axdebug/util.py
new file mode 100644
index 0000000..1345e49
--- /dev/null
+++ b/Lib/site-packages/win32comext/axdebug/util.py
@@ -0,0 +1,114 @@
+# Utility function for wrapping objects. Centralising allows me to turn
+# debugging on and off for the entire package in a single spot.
+
+import sys
+import win32com.server.util
+from win32com.server.exception import Exception
+import winerror
+import win32api
+import os
+
+try:
+ os.environ["DEBUG_AXDEBUG"]
+ debugging = 1
+except KeyError:
+ debugging = 0
+
+def trace(*args):
+ if not debugging: return
+ print(str(win32api.GetCurrentThreadId()) + ":", end=' ')
+ for arg in args:
+ print(arg, end=' ')
+ print()
+
+# The AXDebugging implementation assumes that the returned COM pointers are in
+# some cases identical. Eg, from a C++ perspective:
+# p->GetSomeInterface( &p1 );
+# p->GetSomeInterface( &p2 );
+# p1==p2
+# By default, this is _not_ true for Python.
+# (Now this is only true for Document objects, and Python
+# now does ensure this.
+
+all_wrapped = {}
+
+def _wrap_nodebug(object, iid):
+ return win32com.server.util.wrap(object, iid)
+
+def _wrap_debug(object, iid):
+ import win32com.server.policy
+ dispatcher = win32com.server.policy.DispatcherWin32trace
+ return win32com.server.util.wrap(object, iid, useDispatcher = dispatcher)
+
+if debugging:
+ _wrap = _wrap_debug
+else:
+ _wrap = _wrap_nodebug
+
+def _wrap_remove(object, iid = None):
+ # Old - no longer used or necessary!
+ return
+
+def _dump_wrapped():
+ from win32com.server.util import unwrap
+ print("Wrapped items:")
+ for key, items in all_wrapped.items():
+ print(key, end=' ')
+ try:
+ ob = unwrap(key)
+ print(ob, sys.getrefcount(ob))
+ except:
+ print("")
+
+
+def RaiseNotImpl(who = None):
+ if who is not None:
+ print("********* Function %s Raising E_NOTIMPL ************" % (who))
+
+ # Print a sort-of "traceback", dumping all the frames leading to here.
+ try:
+ 1/0
+ except:
+ frame = sys.exc_info()[2].tb_frame
+ while frame:
+ print("File: %s, Line: %d" % (frame.f_code.co_filename, frame.f_lineno))
+ frame = frame.f_back
+
+ # and raise the exception for COM
+ raise Exception(scode=winerror.E_NOTIMPL)
+
+
+import win32com.server.policy
+class Dispatcher(win32com.server.policy.DispatcherWin32trace):
+ def __init__(self, policyClass, object):
+ win32com.server.policy.DispatcherTrace.__init__(self, policyClass, object)
+ import win32traceutil # Sets up everything.
+# print "Object with win32trace dispatcher created (object=%s)" % `object`
+
+ def _QueryInterface_(self, iid):
+ rc = win32com.server.policy.DispatcherBase._QueryInterface_(self, iid)
+# if not rc:
+# self._trace_("in _QueryInterface_ with unsupported IID %s (%s)\n" % (IIDToInterfaceName(iid),iid))
+ return rc
+
+ def _Invoke_(self, dispid, lcid, wFlags, args):
+ print("In Invoke with", dispid, lcid, wFlags, args, "with object",self.policy._obj_)
+ try:
+ rc = win32com.server.policy.DispatcherBase._Invoke_(self, dispid, lcid, wFlags, args)
+# print "Invoke of", dispid, "returning", rc
+ return rc
+ except Exception:
+ t, v, tb = sys.exc_info()
+ tb = None # A cycle
+ scode = v.scode
+ try:
+ desc = " (" + str(v.description) + ")"
+ except AttributeError:
+ desc = ""
+ print("*** Invoke of %s raised COM exception 0x%x%s" % (dispid, scode, desc))
+ except:
+ print("*** Invoke of %s failed:" % dispid)
+ typ, val, tb = sys.exc_info()
+ import traceback
+ traceback.print_exception(typ, val, tb)
+ raise
diff --git a/Lib/site-packages/win32comext/axscript/Demos/client/asp/CreateObject.asp b/Lib/site-packages/win32comext/axscript/Demos/client/asp/CreateObject.asp
new file mode 100644
index 0000000..e773ea9
--- /dev/null
+++ b/Lib/site-packages/win32comext/axscript/Demos/client/asp/CreateObject.asp
@@ -0,0 +1,19 @@
+
+
+
+
+
+
diff --git a/Lib/site-packages/win32comext/axscript/Demos/client/asp/caps.asp b/Lib/site-packages/win32comext/axscript/Demos/client/asp/caps.asp
new file mode 100644
index 0000000..042d7bf
--- /dev/null
+++ b/Lib/site-packages/win32comext/axscript/Demos/client/asp/caps.asp
@@ -0,0 +1,52 @@
+<%@ Language=Python %>
+
+
+
+
+
+
+Python test
+
+
+
+
+
+
+
+
+
+<%
+import sys
+print sys.path
+from win32com.axscript.asputil import *
+print "Hello"
+print "There"
+print "How are you"
+%>
+
+<%bc = Server.CreateObject("MSWC.BrowserType")%>
+
+
+
+
+
diff --git a/Lib/site-packages/win32comext/axscript/Demos/client/asp/interrupt/test.asp b/Lib/site-packages/win32comext/axscript/Demos/client/asp/interrupt/test.asp
new file mode 100644
index 0000000..8ef256a
--- /dev/null
+++ b/Lib/site-packages/win32comext/axscript/Demos/client/asp/interrupt/test.asp
@@ -0,0 +1,4 @@
+<%@ language=python%>
+
+<%Response.Redirect("test1.html")%>
+
diff --git a/Lib/site-packages/win32comext/axscript/Demos/client/asp/interrupt/test.html b/Lib/site-packages/win32comext/axscript/Demos/client/asp/interrupt/test.html
new file mode 100644
index 0000000..391c117
--- /dev/null
+++ b/Lib/site-packages/win32comext/axscript/Demos/client/asp/interrupt/test.html
@@ -0,0 +1,10 @@
+
+
+
+GOT There
+
+
+
+
diff --git a/Lib/site-packages/win32comext/axscript/Demos/client/asp/interrupt/test1.asp b/Lib/site-packages/win32comext/axscript/Demos/client/asp/interrupt/test1.asp
new file mode 100644
index 0000000..218422f
--- /dev/null
+++ b/Lib/site-packages/win32comext/axscript/Demos/client/asp/interrupt/test1.asp
@@ -0,0 +1,6 @@
+<%@ language =Python%>
+
+
+<%Response.Redirect("test.html")%>
+
+
diff --git a/Lib/site-packages/win32comext/axscript/Demos/client/asp/interrupt/test1.html b/Lib/site-packages/win32comext/axscript/Demos/client/asp/interrupt/test1.html
new file mode 100644
index 0000000..aef43b8
--- /dev/null
+++ b/Lib/site-packages/win32comext/axscript/Demos/client/asp/interrupt/test1.html
@@ -0,0 +1,11 @@
+
+
+
+GOT HERE
+
+
+
+
+
diff --git a/Lib/site-packages/win32comext/axscript/Demos/client/asp/tut1.asp b/Lib/site-packages/win32comext/axscript/Demos/client/asp/tut1.asp
new file mode 100644
index 0000000..4d7dc9d
--- /dev/null
+++ b/Lib/site-packages/win32comext/axscript/Demos/client/asp/tut1.asp
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Lib/site-packages/win32comext/axscript/Demos/client/ie/MarqueeText1.htm b/Lib/site-packages/win32comext/axscript/Demos/client/ie/MarqueeText1.htm
new file mode 100644
index 0000000..33eeb61
--- /dev/null
+++ b/Lib/site-packages/win32comext/axscript/Demos/client/ie/MarqueeText1.htm
@@ -0,0 +1,25 @@
+
+
+
+ Internet Workshop
+
+
+
+
+
+
+
Python AX Script Engine
+ Demo using the Marquee Control
+ Mark Hammond.
+
+
This is really quite a boring demo, as the Marquee control does everything. However, there is Python code behind the buttons that change the speed. This code is all of 2 lines per button!!!
+
+
For more information on Python as an ActiveX scripting language, see
+
+
The Python ActiveX Scripting Engine is not currently registered.
+
+
Due to a privacy
+concern discovered in the engine, the use of Python inside IE has been disabled.
+
+Before any of the supplied demos will work, the engine must be successfully registered.
+
+
To install a version of the engine, that does work with IE, you can execute the Python program
+win32com\axscript\client\pyscript_rexec.py must be run. You can either do this manually, or follow the instructions below.
+
+
Register the engine now!
+
+
If you have read about the privacy
+concern and still wish to register the engine, just follow the process outlined below:
+
+
Click on the link below
+
A dialog will be presented asking if the file should be opened or saved to disk. Select "Open it".
+
A Console program will briefly open, while the server is registered.
+
+After the registration is complete, simply hit the Reload button. If the
+registration was successful, the page will change to the Python/AvtiveX Demo Page.
+
+
+
+
+
+
+
diff --git a/Lib/site-packages/win32comext/axscript/Demos/client/ie/demo_intro.htm b/Lib/site-packages/win32comext/axscript/Demos/client/ie/demo_intro.htm
new file mode 100644
index 0000000..1d33f30
--- /dev/null
+++ b/Lib/site-packages/win32comext/axscript/Demos/client/ie/demo_intro.htm
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
Congratulations on installing the Python ActiveX Scripting Engine
+
+
Be warned that there is a privacy
+concern with this engine. Please read this information, including how to disable the feature.
+
+
+
Object model
+
Except as described below, the object module exposed should be similar to that exposed
+by Visual Basic, etc. Due to the nature of ActiveX Scripting, the details for each
+host are different, but Python should work "correctly".
+
+
The object model exposed via Python for MSIE is not as seamless as VB. The biggest limitation is
+the concept of a "local" namespace. For example, in VB, you can
+code text="Hi there", but in Python, you must code
+MyForm.ThisButton.Text="Hi There". See the foo2 sample
+for futher details.
+
+
Known bugs and problems
+
+
This release seems to have broken Aaron's mouse-trace sample. No idea why, and Im supposed to be looking into it.
+
Builtin objects such as MARQUEE are giving me grief. Objects accessed via forms are generally
+no problem.
+
If you are trying to use Python with the Windows Scripting Host, note that
+.pys files are not correct registered - you will need to explicitely
+specify either cscript.exe or wscript.exe on the command line.
+
The Calculator Demo is a very
+cool sample written by Aaron Watters.
+
+
Mouse track is another of
+Aaron's samples, and shows how fast the Python engine is!
+
+
The foo2 sample is mainly used
+for debugging and testing, but does show some forms in action.
+
+
diff --git a/Lib/site-packages/win32comext/axscript/Demos/client/ie/docwrite.htm b/Lib/site-packages/win32comext/axscript/Demos/client/ie/docwrite.htm
new file mode 100644
index 0000000..bd22a57
--- /dev/null
+++ b/Lib/site-packages/win32comext/axscript/Demos/client/ie/docwrite.htm
@@ -0,0 +1,25 @@
+
+