diff --git a/app.py b/app.py index 160deb1b9..a65fe6356 100644 --- a/app.py +++ b/app.py @@ -139,8 +139,10 @@ def main(): st.markdown(chat["content"]) button_key_copy = f"text_copy_{index}" # Unique key for each copy button button_key_regenerate = f"text_regenerate_{index}" # Unique key for each regenerate button - if st.button('Copy', key=button_key_copy): + if st.button('📋 Copy', key=button_key_copy): clipboard.copy(chat["content"]) + st.session_state.chat_history.append({"role": "bot", "content": bot_response}) + except Exception as e: st.error(f"An error occurred: {e}") diff --git a/chat_history.db b/chat_history.db index e35543b50..cf2c843f9 100644 Binary files a/chat_history.db and b/chat_history.db differ diff --git a/myenv/Lib/site-packages/PyWin32.chm b/myenv/Lib/site-packages/PyWin32.chm new file mode 100644 index 000000000..7606f82b1 Binary files /dev/null and b/myenv/Lib/site-packages/PyWin32.chm differ diff --git a/myenv/Lib/site-packages/adodbapi/__init__.py b/myenv/Lib/site-packages/adodbapi/__init__.py new file mode 100644 index 000000000..0d769e058 --- /dev/null +++ b/myenv/Lib/site-packages/adodbapi/__init__.py @@ -0,0 +1,74 @@ +"""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 + +from .adodbapi import Connection, Cursor, __version__, connect, dateconverter +from .apibase import ( + BINARY, + DATETIME, + NUMBER, + ROWID, + STRING, + DatabaseError, + DataError, + Error, + FetchFailedError, + IntegrityError, + InterfaceError, + InternalError, + NotSupportedError, + OperationalError, + ProgrammingError, + Warning, + apilevel, + paramstyle, + threadsafety, +) + + +def Binary(aString): + """This function constructs an object capable of holding a binary (long) string value.""" + return bytes(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/myenv/Lib/site-packages/adodbapi/ado_consts.py b/myenv/Lib/site-packages/adodbapi/ado_consts.py new file mode 100644 index 000000000..ecb2147dc --- /dev/null +++ b/myenv/Lib/site-packages/adodbapi/ado_consts.py @@ -0,0 +1,281 @@ +# 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/myenv/Lib/site-packages/adodbapi/adodbapi.py b/myenv/Lib/site-packages/adodbapi/adodbapi.py new file mode 100644 index 000000000..8f7c045ea --- /dev/null +++ b/myenv/Lib/site-packages/adodbapi/adodbapi.py @@ -0,0 +1,1223 @@ +"""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 +* https://github.com/mhammond/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.7 and later, +or IronPython version 2.7 and later, +or, after running through 2to3.py, CPython 3.4 or later. +""" + +__version__ = "2.6.2.0" +version = "adodbapi v" + __version__ + +import copy +import decimal +import os +import sys +import weakref + +from . import ado_consts as adc, apibase as api, process_connect_string + +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 clr import Reference + from System import ( + Activator, + Array, + Byte, + DateTime, + DBNull, + Decimal as SystemDecimal, + Type, + ) + + def Dispatch(dispatch): + type = Type.GetTypeFromProgID(dispatch) + return Activator.CreateInstance(type) + + def getIndexedValue(obj, index): + return obj.Item[index] + +else: # try pywin32 + try: + import pythoncom + import pywintypes + import win32com.client + + 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) + + +from collections.abc import Mapping + +# --- define objects to smooth out Python3000 <-> Python 2.x differences +unicodeType = str +longType = int +StringTypes = str +maxint = sys.maxsize + + +# ----------------- 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 + + +# # # # # ----- 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 = weakref.WeakValueDictionary() + 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.mode = kwargs.get("mode", adc.adModeUnknown) + 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 + self.connector.Mode = self.mode + + try: + self.connector.Open() # Open the ADO connection + except api.Error: + self._raiseConnectionError( + api.DatabaseError, + "ADO error trying to Open=%s" % self.connection_string, + ) + + try: # Stefan Fuchs; support WINCCOLEDBProvider + if getIndexedValue(self.connector.Properties, "Transaction DDL").Value != 0: + self.supportsTransactions = True + except pywintypes.com_error: + pass # Stefan Fuchs + self.dbms_name = getIndexedValue(self.connector.Properties, "DBMS Name").Value + try: # Stefan Fuchs + self.dbms_version = getIndexedValue( + self.connector.Properties, "DBMS Version" + ).Value + except pywintypes.com_error: + pass # Stefan Fuchs + 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) # close without back-link clearing + 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 ( + not self.transaction_level + ): # if self.transaction_level == 0 or self.transaction_level is None: + 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" + oid = id(crsr) + self.cursors[oid] = crsr + + def _i_am_closing(self, crsr): + "message from a cursor giving connection a chance to clean up" + try: + del self.cursors[id(crsr)] + 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() + object.__getattribute__( + 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 + 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 + if not dont_tell_me: + self.connection._i_am_closing( + self + ) # take me off the connection's cursors list + 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_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 = self.commandText + self.cmd.Prepared = bool(self._ado_prepared) + except: + self._raiseCursorError( + api.DatabaseError, + 'Error creating new ADODB.Command object for "%s"' + % repr(self.commandText), + ) + + 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.commandText) + 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.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.commandText = procname + self._new_command(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 = [] + + # Note: ADO does not preserve the parameter list, even if "Prepared" is True, so we must build every time. + 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" by ADO 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" + or not hasattr(self, "commandText") + ): + if self.command is not operation: + self._ado_prepared = False + self.command = operation + self._parameter_names = [] + self.commandText = ( + operation + if (self.paramstyle == "qmark" or not parameters) + else self._reformat_operation(operation, parameters) + ) + self._new_command() + 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.commandText + else: + ret = "%s,parameters=%s" % (self.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/myenv/Lib/site-packages/adodbapi/apibase.py b/myenv/Lib/site-packages/adodbapi/apibase.py new file mode 100644 index 000000000..a56cd4b68 --- /dev/null +++ b/myenv/Lib/site-packages/adodbapi/apibase.py @@ -0,0 +1,794 @@ +"""adodbapi.apibase - 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/pywin32 +* http://sourceforge.net/projects/adodbapi +""" + +import datetime +import decimal +import numbers +import sys +import time + +# noinspection PyUnresolvedReferences +from . import ado_consts as adc + +verbose = False # debugging flag + +onIronPython = sys.platform == "cli" +if onIronPython: # we need type definitions for odd data we may need to convert + # noinspection PyUnresolvedReferences + from System import DateTime, DBNull + + NullTypes = (type(None), DBNull) +else: + DateTime = type(NotImplemented) # should never be seen on win32 + NullTypes = type(None) + +# --- define objects to smooth out Python3 <-> Python 2.x differences +unicodeType = str +longType = int +StringTypes = str +makeByteBuffer = bytes +memoryViewType = memoryview +_BaseException = Exception + +try: # jdhardy -- handle bytes under IronPython & Py3 + bytes +except NameError: + bytes = str # define it for old Pythons + + +# ------- Error handlers ------ +def standardErrorHandler(connection, cursor, errorclass, errorvalue): + err = (errorclass, errorvalue) + try: + connection.messages.append(err) + except: + pass + if cursor is not None: + try: + cursor.messages.append(err) + except: + pass + raise errorclass(errorvalue) + + +# Note: _BaseException is defined differently between Python 2.x and 3.x +class Error(_BaseException): + pass # Exception that is the base class of all other error + # exceptions. You can use this to catch all errors with one + # single 'except' statement. Warnings are not considered + # errors and thus should not use this class as base. It must + # be a subclass of the Python StandardError (defined in the + # module exceptions). + + +class Warning(_BaseException): + pass + + +class InterfaceError(Error): + pass + + +class DatabaseError(Error): + pass + + +class InternalError(DatabaseError): + pass + + +class OperationalError(DatabaseError): + pass + + +class ProgrammingError(DatabaseError): + pass + + +class IntegrityError(DatabaseError): + pass + + +class DataError(DatabaseError): + pass + + +class NotSupportedError(DatabaseError): + pass + + +class FetchFailedError(OperationalError): + """ + Error is used by RawStoredProcedureQuerySet to determine when a fetch + failed due to a connection being closed or there is no record set + returned. (Non-standard, added especially for django) + """ + + pass + + +# # # # # ----- Type Objects and Constructors ----- # # # # # +# Many databases need to have the input in a particular format for binding to an operation's input parameters. +# For example, if an input is destined for a DATE column, then it must be bound to the database in a particular +# string format. Similar problems exist for "Row ID" columns or large binary items (e.g. blobs or RAW columns). +# This presents problems for Python since the parameters to the executeXXX() method are untyped. +# When the database module sees a Python string object, it doesn't know if it should be bound as a simple CHAR +# column, as a raw BINARY item, or as a DATE. +# +# To overcome this problem, a module must provide the constructors defined below to create objects that can +# hold special values. When passed to the cursor methods, the module can then detect the proper type of +# the input parameter and bind it accordingly. + +# A Cursor Object's description attribute returns information about each of the result columns of a query. +# The type_code must compare equal to one of Type Objects defined below. Type Objects may be equal to more than +# one type code (e.g. DATETIME could be equal to the type codes for date, time and timestamp columns; +# see the Implementation Hints below for details). + +# SQL NULL values are represented by the Python None singleton on input and output. + +# Note: Usage of Unix ticks for database interfacing can cause troubles because of the limited date range they cover. + + +# def Date(year,month,day): +# "This function constructs an object holding a date value. " +# return dateconverter.date(year,month,day) #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) # 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.datetime(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]) +# +# def Binary(aString): +# """This function constructs an object capable of holding a binary (long) string value. """ +# b = makeByteBuffer(aString) +# return b +# ----- Time converters ---------------------------------------------- +class TimeConverter(object): # this is a generic time converter skeleton + def __init__(self): # the details will be filled in by instances + self._ordinal_1899_12_31 = datetime.date(1899, 12, 31).toordinal() - 1 + # Use cls.types to compare if an input parameter is a datetime + self.types = { + type(self.Date(2000, 1, 1)), + type(self.Time(12, 1, 1)), + type(self.Timestamp(2000, 1, 1, 12, 1, 1)), + datetime.datetime, + datetime.time, + datetime.date, + } + + def COMDate(self, obj): + """Returns a ComDate from a date-time""" + try: # most likely a datetime + tt = obj.timetuple() + + try: + ms = obj.microsecond + except: + ms = 0 + return self.ComDateFromTuple(tt, ms) + except: # might be a tuple + try: + return self.ComDateFromTuple(obj) + except: # try an mxdate + try: + return obj.COMDate() + except: + raise ValueError('Cannot convert "%s" to COMdate.' % repr(obj)) + + def ComDateFromTuple(self, t, microseconds=0): + d = datetime.date(t[0], t[1], t[2]) + integerPart = d.toordinal() - self._ordinal_1899_12_31 + ms = (t[3] * 3600 + t[4] * 60 + t[5]) * 1000000 + microseconds + fractPart = float(ms) / 86400000000.0 + return integerPart + fractPart + + def DateObjectFromCOMDate(self, comDate): + "Returns an object of the wanted type from a ComDate" + raise NotImplementedError # "Abstract class" + + def Date(self, year, month, day): + "This function constructs an object holding a date value." + raise NotImplementedError # "Abstract class" + + def Time(self, hour, minute, second): + "This function constructs an object holding a time value." + raise NotImplementedError # "Abstract class" + + def Timestamp(self, year, month, day, hour, minute, second): + "This function constructs an object holding a time stamp value." + raise NotImplementedError # "Abstract class" + # all purpose date to ISO format converter + + def DateObjectToIsoFormatString(self, obj): + "This function should return a string in the format 'YYYY-MM-dd HH:MM:SS:ms' (ms optional)" + try: # most likely, a datetime.datetime + s = obj.isoformat(" ") + except (TypeError, AttributeError): + if isinstance(obj, datetime.date): + s = obj.isoformat() + " 00:00:00" # return exact midnight + else: + try: # maybe it has a strftime method, like mx + s = obj.strftime("%Y-%m-%d %H:%M:%S") + except AttributeError: + try: # but may be time.struct_time + s = time.strftime("%Y-%m-%d %H:%M:%S", obj) + except: + raise ValueError('Cannot convert "%s" to isoformat' % repr(obj)) + return s + + +# -- Optional: if mx extensions are installed you may use mxDateTime ---- +try: + import mx.DateTime + + mxDateTime = True +except: + mxDateTime = False +if mxDateTime: + + class mxDateTimeConverter(TimeConverter): # used optionally if installed + def __init__(self): + TimeConverter.__init__(self) + self.types.add(type(mx.DateTime)) + + def DateObjectFromCOMDate(self, comDate): + return mx.DateTime.DateTimeFromCOMDate(comDate) + + def Date(self, year, month, day): + return mx.DateTime.Date(year, month, day) + + def Time(self, hour, minute, second): + return mx.DateTime.Time(hour, minute, second) + + def Timestamp(self, year, month, day, hour, minute, second): + return mx.DateTime.Timestamp(year, month, day, hour, minute, second) + +else: + + class mxDateTimeConverter(TimeConverter): + pass # if no mx is installed + + +class pythonDateTimeConverter(TimeConverter): # standard since Python 2.3 + def __init__(self): + TimeConverter.__init__(self) + + def DateObjectFromCOMDate(self, comDate): + if isinstance(comDate, datetime.datetime): + odn = comDate.toordinal() + tim = comDate.time() + new = datetime.datetime.combine(datetime.datetime.fromordinal(odn), tim) + return new + # return comDate.replace(tzinfo=None) # make non aware + elif isinstance(comDate, DateTime): + fComDate = comDate.ToOADate() # ironPython clr Date/Time + else: + fComDate = float(comDate) # ComDate is number of days since 1899-12-31 + integerPart = int(fComDate) + floatpart = fComDate - integerPart + ##if floatpart == 0.0: + ## return datetime.date.fromordinal(integerPart + self._ordinal_1899_12_31) + dte = datetime.datetime.fromordinal( + integerPart + self._ordinal_1899_12_31 + ) + datetime.timedelta(milliseconds=floatpart * 86400000) + # millisecondsperday=86400000 # 24*60*60*1000 + return dte + + def Date(self, year, month, day): + return datetime.date(year, month, day) + + def Time(self, hour, minute, second): + return datetime.time(hour, minute, second) + + def Timestamp(self, year, month, day, hour, minute, second): + return datetime.datetime(year, month, day, hour, minute, second) + + +class pythonTimeConverter(TimeConverter): # the old, ?nix type date and time + def __init__(self): # caution: this Class gets confised by timezones and DST + TimeConverter.__init__(self) + self.types.add(time.struct_time) + + def DateObjectFromCOMDate(self, comDate): + "Returns ticks since 1970" + if isinstance(comDate, datetime.datetime): + return comDate.timetuple() + elif isinstance(comDate, DateTime): # ironPython clr date/time + fcomDate = comDate.ToOADate() + else: + fcomDate = float(comDate) + secondsperday = 86400 # 24*60*60 + # ComDate is number of days since 1899-12-31, gmtime epoch is 1970-1-1 = 25569 days + t = time.gmtime(secondsperday * (fcomDate - 25569.0)) + return t # year,month,day,hour,minute,second,weekday,julianday,daylightsaving=t + + def Date(self, year, month, day): + return self.Timestamp(year, month, day, 0, 0, 0) + + def Time(self, hour, minute, second): + return time.gmtime((hour * 60 + minute) * 60 + second) + + def Timestamp(self, year, month, day, hour, minute, second): + return time.localtime( + time.mktime((year, month, day, hour, minute, second, 0, 0, -1)) + ) + + +base_dateconverter = pythonDateTimeConverter() + +# ------ DB API required module attributes --------------------- +threadsafety = 1 # TODO -- find out whether this module is actually BETTER than 1. + +apilevel = "2.0" # String constant stating the supported DB API level. + +paramstyle = "qmark" # the default parameter style + +# ------ control for an extension which may become part of DB API 3.0 --- +accepted_paramstyles = ("qmark", "named", "format", "pyformat", "dynamic") + +# ------------------------------------------------------------------------------------------ +# define similar types for generic conversion routines +adoIntegerTypes = ( + adc.adInteger, + adc.adSmallInt, + adc.adTinyInt, + adc.adUnsignedInt, + adc.adUnsignedSmallInt, + adc.adUnsignedTinyInt, + adc.adBoolean, + adc.adError, +) # max 32 bits +adoRowIdTypes = (adc.adChapter,) # v2.1 Rose +adoLongTypes = (adc.adBigInt, adc.adFileTime, adc.adUnsignedBigInt) +adoExactNumericTypes = ( + adc.adDecimal, + adc.adNumeric, + adc.adVarNumeric, + adc.adCurrency, +) # v2.3 Cole +adoApproximateNumericTypes = (adc.adDouble, adc.adSingle) # v2.1 Cole +adoStringTypes = ( + adc.adBSTR, + adc.adChar, + adc.adLongVarChar, + adc.adLongVarWChar, + adc.adVarChar, + adc.adVarWChar, + adc.adWChar, +) +adoBinaryTypes = (adc.adBinary, adc.adLongVarBinary, adc.adVarBinary) +adoDateTimeTypes = (adc.adDBTime, adc.adDBTimeStamp, adc.adDate, adc.adDBDate) +adoRemainingTypes = ( + adc.adEmpty, + adc.adIDispatch, + adc.adIUnknown, + adc.adPropVariant, + adc.adArray, + adc.adUserDefined, + adc.adVariant, + adc.adGUID, +) + + +# this class is a trick to determine whether a type is a member of a related group of types. see PEP notes +class DBAPITypeObject(object): + def __init__(self, valuesTuple): + self.values = frozenset(valuesTuple) + + def __eq__(self, other): + return other in self.values + + def __ne__(self, other): + return other not in self.values + + +"""This type object is used to describe columns in a database that are string-based (e.g. CHAR). """ +STRING = DBAPITypeObject(adoStringTypes) + +"""This type object is used to describe (long) binary columns in a database (e.g. LONG, RAW, BLOBs). """ +BINARY = DBAPITypeObject(adoBinaryTypes) + +"""This type object is used to describe numeric columns in a database. """ +NUMBER = DBAPITypeObject( + adoIntegerTypes + adoLongTypes + adoExactNumericTypes + adoApproximateNumericTypes +) + +"""This type object is used to describe date/time columns in a database. """ + +DATETIME = DBAPITypeObject(adoDateTimeTypes) +"""This type object is used to describe the "Row ID" column in a database. """ +ROWID = DBAPITypeObject(adoRowIdTypes) + +OTHER = DBAPITypeObject(adoRemainingTypes) + +# ------- utilities for translating python data types to ADO data types --------------------------------- +typeMap = { + memoryViewType: adc.adVarBinary, + float: adc.adDouble, + type(None): adc.adEmpty, + str: adc.adBSTR, + bool: adc.adBoolean, # v2.1 Cole + decimal.Decimal: adc.adDecimal, + int: adc.adBigInt, + bytes: adc.adVarBinary, +} + + +def pyTypeToADOType(d): + tp = type(d) + try: + return typeMap[tp] + except KeyError: # The type was not defined in the pre-computed Type table + from . import dateconverter + + if ( + tp in dateconverter.types + ): # maybe it is one of our supported Date/Time types + return adc.adDate + # otherwise, attempt to discern the type by probing the data object itself -- to handle duck typing + if isinstance(d, StringTypes): + return adc.adBSTR + if isinstance(d, numbers.Integral): + return adc.adBigInt + if isinstance(d, numbers.Real): + return adc.adDouble + raise DataError('cannot convert "%s" (type=%s) to ADO' % (repr(d), tp)) + + +# # # # # # # # # # # # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# functions to convert database values to Python objects +# ------------------------------------------------------------------------ +# variant type : function converting variant to Python value +def variantConvertDate(v): + from . import dateconverter # this function only called when adodbapi is running + + return dateconverter.DateObjectFromCOMDate(v) + + +def cvtString(variant): # use to get old action of adodbapi v1 if desired + if onIronPython: + try: + return variant.ToString() + except: + pass + return str(variant) + + +def cvtDecimal(variant): # better name + return _convertNumberWithCulture(variant, decimal.Decimal) + + +def cvtNumeric(variant): # older name - don't break old code + return cvtDecimal(variant) + + +def cvtFloat(variant): + return _convertNumberWithCulture(variant, float) + + +def _convertNumberWithCulture(variant, f): + try: + return f(variant) + except (ValueError, TypeError, decimal.InvalidOperation): + try: + europeVsUS = str(variant).replace(",", ".") + return f(europeVsUS) + except (ValueError, TypeError, decimal.InvalidOperation): + pass + + +def cvtInt(variant): + return int(variant) + + +def cvtLong(variant): # only important in old versions where long and int differ + return int(variant) + + +def cvtBuffer(variant): + return bytes(variant) + + +def cvtUnicode(variant): + return str(variant) + + +def identity(x): + return x + + +def cvtUnusual(variant): + if verbose > 1: + sys.stderr.write("Conversion called for Unusual data=%s\n" % repr(variant)) + if isinstance(variant, DateTime): # COMdate or System.Date + from .adodbapi import ( # this will only be called when adodbapi is in use, and very rarely + dateconverter, + ) + + return dateconverter.DateObjectFromCOMDate(variant) + return variant # cannot find conversion function -- just give the data to the user + + +def convert_to_python(variant, func): # convert DB value into Python value + if isinstance(variant, NullTypes): # IronPython Null or None + return None + return func(variant) # call the appropriate conversion function + + +class MultiMap(dict): # builds a dictionary from {(sequence,of,keys) : function} + """A dictionary of ado.type : function -- but you can set multiple items by passing a sequence of keys""" + + # useful for defining conversion functions for groups of similar data types. + def __init__(self, aDict): + for k, v in list(aDict.items()): + self[k] = v # we must call __setitem__ + + def __setitem__(self, adoType, cvtFn): + "set a single item, or a whole sequence of items" + try: # user passed us a sequence, set them individually + for type in adoType: + dict.__setitem__(self, type, cvtFn) + except TypeError: # a single value fails attempt to iterate + dict.__setitem__(self, adoType, cvtFn) + + +# initialize variantConversions dictionary used to convert SQL to Python +# this is the dictionary of default conversion functions, built by the class above. +# this becomes a class attribute for the Connection, and that attribute is used +# to build the list of column conversion functions for the Cursor +variantConversions = MultiMap( + { + adoDateTimeTypes: variantConvertDate, + adoApproximateNumericTypes: cvtFloat, + adoExactNumericTypes: cvtDecimal, # use to force decimal rather than unicode + adoLongTypes: cvtLong, + adoIntegerTypes: cvtInt, + adoRowIdTypes: cvtInt, + adoStringTypes: identity, + adoBinaryTypes: cvtBuffer, + adoRemainingTypes: cvtUnusual, + } +) + +# # # # # classes to emulate the result of cursor.fetchxxx() as a sequence of sequences # # # # # +# "an ENUM of how my low level records are laid out" +RS_WIN_32, RS_ARRAY, RS_REMOTE = list(range(1, 4)) + + +class SQLrow(object): # a single database row + # class to emulate a sequence, so that a column may be retrieved by either number or name + def __init__(self, rows, index): # "rows" is an _SQLrows object, index is which row + self.rows = rows # parent 'fetch' container object + self.index = index # my row number within parent + + def __getattr__(self, name): # used for row.columnName type of value access + try: + return self._getValue(self.rows.columnNames[name.lower()]) + except KeyError: + raise AttributeError('Unknown column name "{}"'.format(name)) + + def _getValue(self, key): # key must be an integer + if ( + self.rows.recordset_format == RS_ARRAY + ): # retrieve from two-dimensional array + v = self.rows.ado_results[key, self.index] + elif self.rows.recordset_format == RS_REMOTE: + v = self.rows.ado_results[self.index][key] + else: # pywin32 - retrieve from tuple of tuples + v = self.rows.ado_results[key][self.index] + if self.rows.converters is NotImplemented: + return v + return convert_to_python(v, self.rows.converters[key]) + + def __len__(self): + return self.rows.numberOfColumns + + def __getitem__(self, key): # used for row[key] type of value access + if isinstance(key, int): # normal row[1] designation + try: + return self._getValue(key) + except IndexError: + raise + if isinstance(key, slice): + indices = key.indices(self.rows.numberOfColumns) + vl = [self._getValue(i) for i in range(*indices)] + return tuple(vl) + try: + return self._getValue( + self.rows.columnNames[key.lower()] + ) # extension row[columnName] designation + except (KeyError, TypeError): + er, st, tr = sys.exc_info() + raise er( + 'No such key as "%s" in %s' % (repr(key), self.__repr__()) + ).with_traceback(tr) + + def __iter__(self): + return iter(self.__next__()) + + def __next__(self): + for n in range(self.rows.numberOfColumns): + yield self._getValue(n) + + def __repr__(self): # create a human readable representation + taglist = sorted(list(self.rows.columnNames.items()), key=lambda x: x[1]) + s = "" + + def __str__(self): # create a pretty human readable representation + return str( + tuple(str(self._getValue(i)) for i in range(self.rows.numberOfColumns)) + ) + + # TO-DO implement pickling an SQLrow directly + # def __getstate__(self): return self.__dict__ + # def __setstate__(self, d): self.__dict__.update(d) + # which basically tell pickle to treat your class just like a normal one, + # taking self.__dict__ as representing the whole of the instance state, + # despite the existence of the __getattr__. + # # # # + + +class SQLrows(object): + # class to emulate a sequence for multiple rows using a container object + def __init__(self, ado_results, numberOfRows, cursor): + self.ado_results = ado_results # raw result of SQL get + try: + self.recordset_format = cursor.recordset_format + self.numberOfColumns = cursor.numberOfColumns + self.converters = cursor.converters + self.columnNames = cursor.columnNames + except AttributeError: + self.recordset_format = RS_ARRAY + self.numberOfColumns = 0 + self.converters = [] + self.columnNames = {} + self.numberOfRows = numberOfRows + + def __len__(self): + return self.numberOfRows + + def __getitem__(self, item): # used for row or row,column access + if not self.ado_results: + return [] + if isinstance(item, slice): # will return a list of row objects + indices = item.indices(self.numberOfRows) + return [SQLrow(self, k) for k in range(*indices)] + elif isinstance(item, tuple) and len(item) == 2: + # d = some_rowsObject[i,j] will return a datum from a two-dimension address + i, j = item + if not isinstance(j, int): + try: + j = self.columnNames[j.lower()] # convert named column to numeric + except KeyError: + raise KeyError('adodbapi: no such column name as "%s"' % repr(j)) + if self.recordset_format == RS_ARRAY: # retrieve from two-dimensional array + v = self.ado_results[j, i] + elif self.recordset_format == RS_REMOTE: + v = self.ado_results[i][j] + else: # pywin32 - retrieve from tuple of tuples + v = self.ado_results[j][i] + if self.converters is NotImplemented: + return v + return convert_to_python(v, self.converters[j]) + else: + row = SQLrow(self, item) # new row descriptor + return row + + def __iter__(self): + return iter(self.__next__()) + + def __next__(self): + for n in range(self.numberOfRows): + row = SQLrow(self, n) + yield row + # # # # # + + # # # # # functions to re-format SQL requests to other paramstyle requirements # # # # # # # # # # + + +def changeNamedToQmark( + op, +): # convert from 'named' paramstyle to ADO required '?'mark parameters + outOp = "" + outparms = [] + chunks = op.split( + "'" + ) # quote all literals -- odd numbered list results are literals. + inQuotes = False + for chunk in chunks: + if inQuotes: # this is inside a quote + if chunk == "": # double apostrophe to quote one apostrophe + outOp = outOp[:-1] # so take one away + else: + outOp += "'" + chunk + "'" # else pass the quoted string as is. + else: # is SQL code -- look for a :namedParameter + while chunk: # some SQL string remains + sp = chunk.split(":", 1) + outOp += sp[0] # concat the part up to the : + s = "" + try: + chunk = sp[1] + except IndexError: + chunk = None + if chunk: # there was a parameter - parse it out + i = 0 + c = chunk[0] + while c.isalnum() or c == "_": + i += 1 + try: + c = chunk[i] + except IndexError: + break + s = chunk[:i] + chunk = chunk[i:] + if s: + outparms.append(s) # list the parameters in order + outOp += "?" # put in the Qmark + inQuotes = not inQuotes + return outOp, outparms + + +def changeFormatToQmark( + op, +): # convert from 'format' paramstyle to ADO required '?'mark parameters + outOp = "" + outparams = [] + chunks = op.split( + "'" + ) # quote all literals -- odd numbered list results are literals. + inQuotes = False + for chunk in chunks: + if inQuotes: + if ( + outOp != "" and chunk == "" + ): # he used a double apostrophe to quote one apostrophe + outOp = outOp[:-1] # so take one away + else: + outOp += "'" + chunk + "'" # else pass the quoted string as is. + else: # is SQL code -- look for a %s parameter + if "%(" in chunk: # ugh! pyformat! + while chunk: # some SQL string remains + sp = chunk.split("%(", 1) + outOp += sp[0] # concat the part up to the % + if len(sp) > 1: + try: + s, chunk = sp[1].split(")s", 1) # find the ')s' + except ValueError: + raise ProgrammingError( + 'Pyformat SQL has incorrect format near "%s"' % chunk + ) + outparams.append(s) + outOp += "?" # put in the Qmark + else: + chunk = None + else: # proper '%s' format + sp = chunk.split("%s") # make each %s + outOp += "?".join(sp) # into ? + inQuotes = not inQuotes # every other chunk is a quoted string + return outOp, outparams diff --git a/myenv/Lib/site-packages/adodbapi/examples/db_print.py b/myenv/Lib/site-packages/adodbapi/examples/db_print.py new file mode 100644 index 000000000..3f5f9d5be --- /dev/null +++ b/myenv/Lib/site-packages/adodbapi/examples/db_print.py @@ -0,0 +1,72 @@ +""" db_print.py -- a simple demo for ADO database reads.""" + +import sys + +import adodbapi.ado_consts as adc + +cmd_args = ("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" +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/myenv/Lib/site-packages/adodbapi/examples/db_table_names.py b/myenv/Lib/site-packages/adodbapi/examples/db_table_names.py new file mode 100644 index 000000000..eb512a330 --- /dev/null +++ b/myenv/Lib/site-packages/adodbapi/examples/db_table_names.py @@ -0,0 +1,20 @@ +""" 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/myenv/Lib/site-packages/adodbapi/examples/xls_read.py b/myenv/Lib/site-packages/adodbapi/examples/xls_read.py new file mode 100644 index 000000000..45e0d277a --- /dev/null +++ b/myenv/Lib/site-packages/adodbapi/examples/xls_read.py @@ -0,0 +1,41 @@ +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/myenv/Lib/site-packages/adodbapi/examples/xls_write.py b/myenv/Lib/site-packages/adodbapi/examples/xls_write.py new file mode 100644 index 000000000..9d1d31144 --- /dev/null +++ b/myenv/Lib/site-packages/adodbapi/examples/xls_write.py @@ -0,0 +1,41 @@ +import datetime + +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 (Name varchar, Rank varchar, SrvcNum integer, Weight float, Birth date)" + ) + + sql = "INSERT INTO SheetOne (name, rank , srvcnum, weight, birth) values (?,?,?,?,?)" + + data = ("Mike Murphy", "SSG", 123456789, 167.8, datetime.date(1922, 12, 27)) + crsr.execute(sql, data) # write the first row of data + crsr.execute( + sql, ["John Jones", "Pvt", 987654321, 140.0, datetime.date(1921, 7, 4)] + ) # another row of data +conn.close() +print("Created spreadsheet=%s worksheet=%s" % (filename, "SheetOne")) diff --git a/myenv/Lib/site-packages/adodbapi/is64bit.py b/myenv/Lib/site-packages/adodbapi/is64bit.py new file mode 100644 index 000000000..bba12b435 --- /dev/null +++ b/myenv/Lib/site-packages/adodbapi/is64bit.py @@ -0,0 +1,41 @@ +"""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, KeyError): + 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/myenv/Lib/site-packages/adodbapi/license.txt b/myenv/Lib/site-packages/adodbapi/license.txt new file mode 100644 index 000000000..c255f4aae --- /dev/null +++ b/myenv/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/myenv/Lib/site-packages/adodbapi/process_connect_string.py b/myenv/Lib/site-packages/adodbapi/process_connect_string.py new file mode 100644 index 000000000..fa34d23a1 --- /dev/null +++ b/myenv/Lib/site-packages/adodbapi/process_connect_string.py @@ -0,0 +1,144 @@ +""" a clumsy attempt at a macro language to let the programmer execute code on the server (ex: determine 64bit)""" +from . import is64bit as is64bit + + +def macro_call(macro_name, args, kwargs): + """allow the programmer to perform limited processing on the server by passing macro names and args + + :new_key - the key name the macro will create + :args[0] - macro name + :args[1:] - any arguments + :code - the value of the keyword item + :kwargs - the connection keyword dictionary. ??key has been removed + --> the value to put in for kwargs['name'] = value + """ + if isinstance(args, (str, str)): + args = [ + args + ] # the user forgot to pass a sequence, so make a string into args[0] + new_key = args[0] + try: + if macro_name == "is64bit": + if is64bit.Python(): # if on 64 bit Python + return new_key, args[1] # return first argument + else: + try: + return new_key, args[2] # else return second argument (if defined) + except IndexError: + return new_key, "" # else return blank + + elif ( + macro_name == "getuser" + ): # get the name of the user the server is logged in under + if not new_key in kwargs: + import getpass + + return new_key, getpass.getuser() + + elif macro_name == "getnode": # get the name of the computer running the server + import platform + + try: + return new_key, args[1] % platform.node() + except IndexError: + return new_key, platform.node() + + elif macro_name == "getenv": # expand the server's environment variable args[1] + try: + dflt = args[2] # if not found, default from args[2] + except IndexError: # or blank + dflt = "" + return new_key, os.environ.get(args[1], dflt) + + elif macro_name == "auto_security": + if ( + not "user" in kwargs or not kwargs["user"] + ): # missing, blank, or Null username + return new_key, "Integrated Security=SSPI" + return new_key, "User ID=%(user)s; Password=%(password)s" % kwargs + + elif ( + macro_name == "find_temp_test_path" + ): # helper function for testing ado operation -- undocumented + import os + import tempfile + + return new_key, os.path.join( + tempfile.gettempdir(), "adodbapi_test", args[1] + ) + + raise ValueError("Unknown connect string macro=%s" % macro_name) + except: + raise ValueError("Error in macro processing %s %s" % (macro_name, repr(args))) + + +def process( + args, kwargs, expand_macros=False +): # --> connection string with keyword arguments processed. + """attempts to inject arguments into a connection string using Python "%" operator for strings + + co: adodbapi connection object + args: positional parameters from the .connect() call + kvargs: keyword arguments from the .connect() call + """ + try: + dsn = args[0] + except IndexError: + dsn = None + if isinstance( + dsn, dict + ): # as a convenience the first argument may be django settings + kwargs.update(dsn) + elif ( + dsn + ): # the connection string is passed to the connection as part of the keyword dictionary + kwargs["connection_string"] = dsn + try: + a1 = args[1] + except IndexError: + a1 = None + # historically, the second positional argument might be a timeout value + if isinstance(a1, int): + kwargs["timeout"] = a1 + # if the second positional argument is a string, then it is user + elif isinstance(a1, str): + kwargs["user"] = a1 + # if the second positional argument is a dictionary, use it as keyword arguments, too + elif isinstance(a1, dict): + kwargs.update(a1) + try: + kwargs["password"] = args[2] # the third positional argument is password + kwargs["host"] = args[3] # the fourth positional argument is host name + kwargs["database"] = args[4] # the fifth positional argument is database name + except IndexError: + pass + + # make sure connection string is defined somehow + if not "connection_string" in kwargs: + try: # perhaps 'dsn' was defined + kwargs["connection_string"] = kwargs["dsn"] + except KeyError: + try: # as a last effort, use the "host" keyword + kwargs["connection_string"] = kwargs["host"] + except KeyError: + raise TypeError("Must define 'connection_string' for ado connections") + if expand_macros: + for kwarg in list(kwargs.keys()): + if kwarg.startswith("macro_"): # If a key defines a macro + macro_name = kwarg[6:] # name without the "macro_" + macro_code = kwargs.pop( + kwarg + ) # we remove the macro_key and get the code to execute + new_key, rslt = macro_call( + macro_name, macro_code, kwargs + ) # run the code in the local context + kwargs[new_key] = rslt # put the result back in the keywords dict + # special processing for PyRO IPv6 host address + try: + s = kwargs["proxy_host"] + if ":" in s: # it is an IPv6 address + if s[0] != "[": # is not surrounded by brackets + kwargs["proxy_host"] = s.join(("[", "]")) # put it in brackets + except KeyError: + pass + return kwargs diff --git a/myenv/Lib/site-packages/adodbapi/readme.txt b/myenv/Lib/site-packages/adodbapi/readme.txt new file mode 100644 index 000000000..cf591905e --- /dev/null +++ b/myenv/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. +* 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.7 or 3.5 or higher + and pywin32 (Mark Hammond's python for windows extensions.) +or + Iron Python 2.7 or higher. (works in IPy2.0 for all data types except BUFFER) + +Installation: +* (C-Python on Windows): Install pywin32 ("pip install pywin32") which includes adodbapi. +* (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. + +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 + ............ +notes for 2.6.2: + The definitive source has been moved to https://github.com/mhammond/pywin32/tree/master/adodbapi. + Remote has proven too hard to configure and test with Pyro4. I am moving it to unsupported status + until I can change to a different connection method. +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]] + +License +------- +LGPL, see http://www.opensource.org/licenses/lgpl-license.php + +Documentation +------------- + +Look at adodbapi/quick_reference.md +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 may be checked, (infrequently). + -- please use: https://github.com/mhammond/pywin32/issues diff --git a/myenv/Lib/site-packages/adodbapi/remote.py b/myenv/Lib/site-packages/adodbapi/remote.py new file mode 100644 index 000000000..ae22b5a7e --- /dev/null +++ b/myenv/Lib/site-packages/adodbapi/remote.py @@ -0,0 +1,634 @@ +"""adodbapi.remote - 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/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 thanks to 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.7 and later, +or, after running through 2to3.py, CPython 3.0 or later. +""" + +__version__ = "2.6.0.4" +version = "adodbapi.remote v" + __version__ + +import array +import datetime +import os +import sys +import time + +# Pyro4 is required for server and remote operation --> https://pypi.python.org/pypi/Pyro4/ +try: + import Pyro4 +except ImportError: + print('* * * Sorry, server operation requires Pyro4. Please "pip import" it.') + exit(11) + +import adodbapi +import adodbapi.apibase as api +import adodbapi.process_connect_string +from adodbapi.apibase import ProgrammingError + +_BaseException = api._BaseException + +sys.excepthook = Pyro4.util.excepthook +Pyro4.config.PREFER_IP_VERSION = 0 # allow system to prefer IPv6 +Pyro4.config.COMMTIMEOUT = 40.0 # a bit longer than the default SQL server Gtimeout +Pyro4.config.SERIALIZER = "pickle" + +try: + verbose = int(os.environ["ADODBAPI_VERBOSE"]) +except: + verbose = False +if verbose: + print(version) + +# --- define objects to smooth out Python3 <-> 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' +StringTypes = str +makeByteBuffer = bytes +memoryViewType = memoryview + +# ----------------------------------------------------------- +# conversion functions mandated by PEP 249 +Binary = makeByteBuffer # override the function from apibase.py + + +def Date(year, month, day): + return datetime.date(year, month, day) # dateconverter.Date(year,month,day) + + +def Time(hour, minute, second): + return datetime.time(hour, minute, second) # dateconverter.Time(hour,minute,second) + + +def Timestamp(year, month, day, hour, minute, second): + return datetime.datetime(year, month, day, hour, minute, second) + + +def DateFromTicks(ticks): + return Date(*time.gmtime(ticks)[:3]) + + +def TimeFromTicks(ticks): + return Time(*time.gmtime(ticks)[3:6]) + + +def TimestampFromTicks(ticks): + return Timestamp(*time.gmtime(ticks)[:6]) + + +def connect(*args, **kwargs): # --> a remote db-api connection object + """Create and open a remote db-api database connection object""" + # process the argument list the programmer gave us + kwargs = adodbapi.process_connect_string.process(args, kwargs) + # the "proxy_xxx" keys tell us where to find the PyRO proxy server + kwargs.setdefault( + "pyro_connection", "PYRO:ado.connection@%(proxy_host)s:%(proxy_port)s" + ) + if not "proxy_port" in kwargs: + try: + pport = os.environ["PROXY_PORT"] + except KeyError: + pport = 9099 + kwargs["proxy_port"] = pport + if not "proxy_host" in kwargs or not kwargs["proxy_host"]: + try: + phost = os.environ["PROXY_HOST"] + except KeyError: + phost = "[::1]" # '127.0.0.1' + kwargs["proxy_host"] = phost + ado_uri = kwargs["pyro_connection"] % kwargs + # ask PyRO make us a remote connection object + auto_retry = 3 + while auto_retry: + try: + dispatcher = Pyro4.Proxy(ado_uri) + if "comm_timeout" in kwargs: + dispatcher._pyroTimeout = float(kwargs["comm_timeout"]) + uri = dispatcher.make_connection() + break + except Pyro4.core.errors.PyroError: + auto_retry -= 1 + if auto_retry: + time.sleep(1) + else: + raise api.DatabaseError("Cannot create connection to=%s" % ado_uri) + + conn_uri = fix_uri(uri, kwargs) # get a host connection from the proxy server + while auto_retry: + try: + host_conn = Pyro4.Proxy( + conn_uri + ) # bring up an exclusive Pyro connection for my ADO connection + break + except Pyro4.core.errors.PyroError: + auto_retry -= 1 + if auto_retry: + time.sleep(1) + else: + raise api.DatabaseError( + "Cannot create ADO connection object using=%s" % conn_uri + ) + if "comm_timeout" in kwargs: + host_conn._pyroTimeout = float(kwargs["comm_timeout"]) + # make a local clone + myConn = Connection() + while auto_retry: + try: + myConn.connect( + kwargs, host_conn + ) # call my connect method -- hand him the host connection + break + except Pyro4.core.errors.PyroError: + auto_retry -= 1 + if auto_retry: + time.sleep(1) + else: + raise api.DatabaseError( + "Pyro error creating connection to/thru=%s" % repr(kwargs) + ) + except _BaseException as e: + raise api.DatabaseError( + "Error creating remote connection to=%s, e=%s, %s" + % (repr(kwargs), repr(e), sys.exc_info()[2]) + ) + return myConn + + +def fix_uri(uri, kwargs): + """convert a generic pyro uri with '0.0.0.0' into the address we actually called""" + u = uri.asString() + s = u.split("[::0]") # IPv6 generic address + if len(s) == 1: # did not find one + s = u.split("0.0.0.0") # IPv4 generic address + if len(s) > 1: # found a generic + return kwargs["proxy_host"].join(s) # fill in our address for the host + return uri + + +# # # # # ----- the Class that defines a connection ----- # # # # # +class Connection(object): + # include connection 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 + # set up some class attributes + paramstyle = api.paramstyle + + @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): + self.proxy = None + self.kwargs = {} + self.errorhandler = None + self.supportsTransactions = False + self.paramstyle = api.paramstyle + self.timeout = 30 + self.cursors = {} + + def connect(self, kwargs, connection_maker): + self.kwargs = kwargs + if verbose: + print('%s attempting: "%s"' % (version, repr(kwargs))) + self.proxy = connection_maker + ##try: + ret = self.proxy.connect(kwargs) # ask the server to hook us up + ##except ImportError, e: # Pyro is trying to import pywinTypes.comerrer + ## self._raiseConnectionError(api.DatabaseError, 'Proxy cannot connect using=%s' % repr(kwargs)) + if ret is not True: + self._raiseConnectionError( + api.OperationalError, "Proxy returns error message=%s" % repr(ret) + ) + + self.supportsTransactions = self.getIndexedValue("supportsTransactions") + self.paramstyle = self.getIndexedValue("paramstyle") + self.timeout = self.getIndexedValue("timeout") + if verbose: + print("adodbapi.remote 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 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() + try: + """close the underlying remote Connection object""" + self.proxy.close() + if verbose: + print("adodbapi.remote Closed connection at %X" % id(self)) + object.__delattr__( + self, "proxy" + ) # future attempts to use closed cursor will be caught by __getattr__ + except Exception: + pass + + def __del__(self): + try: + self.proxy.close() + except: + pass + + 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. + """ + if not self.supportsTransactions: + return + result = self.proxy.commit() + if result: + self._raiseConnectionError( + api.OperationalError, "Error during commit: %s" % result + ) + + 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. + """ + result = self.proxy.rollback() + if result: + self._raiseConnectionError( + api.OperationalError, "Error during rollback: %s" % result + ) + + def __setattr__(self, name, value): + if name in ("paramstyle", "timeout", "autocommit"): + if self.proxy: + self.proxy.send_attribute_to_host(name, value) + object.__setattr__(self, name, value) # store attribute locally (too) + + 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 self.ProgrammingError( + "this data provider does not support Rollback" + ) + elif item in ( + "dbms_name", + "dbms_version", + "connection_string", + "autocommit", + ): # 'messages' ): + return self.getIndexedValue(item) + elif item == "proxy": + raise self.ProgrammingError("Attempting to use closed connection") + else: + raise self.ProgrammingError('No remote access for attribute="%s"' % item) + + def getIndexedValue(self, index): + r = self.proxy.get_attribute_for_remote(index) + return r + + def cursor(self): + "Return a new Cursor Object using the connection." + myCursor = Cursor(self) + return myCursor + + def _i_am_here(self, crsr): + "message from a new cursor proclaiming its existence" + self.cursors[crsr.id] = crsr + + 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 __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): + return self.proxy.get_table_names() + + +def fixpickle(x): + """pickle barfs on buffer(x) so we pass as array.array(x) then restore to original form for .execute()""" + if x is None: + return None + if isinstance(x, dict): + # for 'named' paramstyle user will pass a mapping + newargs = {} + for arg, val in list(x.items()): + if isinstance(val, memoryViewType): + newval = array.array("B") + newval.fromstring(val) + newargs[arg] = newval + else: + newargs[arg] = val + return newargs + # if not a mapping, then a sequence + newargs = [] + for arg in x: + if isinstance(arg, memoryViewType): + newarg = array.array("B") + newarg.fromstring(arg) + newargs.append(newarg) + else: + newargs.append(arg) + return newargs + + +class Cursor(object): + def __init__(self, connection): + self.command = None + self.errorhandler = None ## was: connection.errorhandler + self.connection = connection + self.proxy = self.connection.proxy + self.rs = None # the fetchable data for this cursor + self.converters = NotImplemented + self.id = connection.proxy.build_cursor() + connection._i_am_here(self) + self.recordset_format = api.RS_REMOTE + if verbose: + print( + "%s New cursor at %X on conn %X" + % (version, id(self), id(self.connection)) + ) + + def prepare(self, operation): + self.command = operation + try: + del self.description + except AttributeError: + pass + self.proxy.crsr_prepare(self.id, operation) + + def __iter__(self): # [2.1 Zamarev] + return iter(self.fetchone, None) # [2.1 Zamarev] + + 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 __getattr__(self, key): + if key == "numberOfColumns": + try: + return len(self.rs[0]) + except: + return 0 + if key == "description": + try: + self.description = self.proxy.crsr_get_description(self.id)[:] + return self.description + except TypeError: + return None + if key == "columnNames": + try: + r = dict( + self.proxy.crsr_get_columnNames(self.id) + ) # copy the remote columns + + except TypeError: + r = {} + self.columnNames = r + return r + + if key == "remote_cursor": + raise api.OperationalError + try: + return self.proxy.crsr_get_attribute_for_remote(self.id, key) + except AttributeError: + raise api.InternalError( + 'Failure getting attribute "%s" from proxy cursor.' % key + ) + + def __setattr__(self, key, value): + if key == "arraysize": + self.proxy.crsr_set_arraysize(self.id, value) + if key == "paramstyle": + if value in api.accepted_paramstyles: + self.proxy.crsr_set_paramstyle(self.id, value) + else: + self._raiseCursorError( + api.ProgrammingError, 'invalid paramstyle ="%s"' % value + ) + object.__setattr__(self, key, value) + + def _raiseCursorError(self, errorclass, errorvalue): + eh = self.errorhandler + if eh is None: + eh = api.standardErrorHandler + eh(self.connection, self, errorclass, errorvalue) + + def execute(self, operation, parameters=None): + if self.connection is None: + self._raiseCursorError( + ProgrammingError, "Attempted operation on closed cursor" + ) + self.command = operation + try: + del self.description + except AttributeError: + pass + try: + del self.columnNames + except AttributeError: + pass + fp = fixpickle(parameters) + if verbose > 2: + print( + ( + '%s executing "%s" with params=%s' + % (version, operation, repr(parameters)) + ) + ) + result = self.proxy.crsr_execute(self.id, operation, fp) + if result: # an exception was triggered + self._raiseCursorError(result[0], result[1]) + + def executemany(self, operation, seq_of_parameters): + if self.connection is None: + self._raiseCursorError( + ProgrammingError, "Attempted operation on closed cursor" + ) + self.command = operation + try: + del self.description + except AttributeError: + pass + try: + del self.columnNames + except AttributeError: + pass + sq = [fixpickle(x) for x in seq_of_parameters] + if verbose > 2: + print( + ( + '%s executemany "%s" with params=%s' + % (version, operation, repr(seq_of_parameters)) + ) + ) + self.proxy.crsr_executemany(self.id, operation, sq) + + def nextset(self): + try: + del self.description + except AttributeError: + pass + try: + del self.columnNames + except AttributeError: + pass + if verbose > 2: + print(("%s nextset" % version)) + return self.proxy.crsr_nextset(self.id) + + def callproc(self, procname, parameters=None): + if self.connection is None: + self._raiseCursorError( + ProgrammingError, "Attempted operation on closed cursor" + ) + self.command = procname + try: + del self.description + except AttributeError: + pass + try: + del self.columnNames + except AttributeError: + pass + fp = fixpickle(parameters) + if verbose > 2: + print( + ( + '%s callproc "%s" with params=%s' + % (version, procname, repr(parameters)) + ) + ) + return self.proxy.crsr_callproc(self.id, procname, fp) + + def fetchone(self): + try: + f1 = self.proxy.crsr_fetchone(self.id) + except _BaseException as e: + self._raiseCursorError(api.DatabaseError, e) + else: + if f1 is None: + return None + self.rs = [f1] + return api.SQLrows(self.rs, 1, self)[ + 0 + ] # new object to hold the results of the fetch + + def fetchmany(self, size=None): + try: + self.rs = self.proxy.crsr_fetchmany(self.id, size) + if not self.rs: + return [] + r = api.SQLrows(self.rs, len(self.rs), self) + return r + except Exception as e: + self._raiseCursorError(api.DatabaseError, e) + + def fetchall(self): + try: + self.rs = self.proxy.crsr_fetchall(self.id) + if not self.rs: + return [] + return api.SQLrows(self.rs, len(self.rs), self) + except Exception as e: + self._raiseCursorError(api.DatabaseError, e) + + def close(self): + if self.connection is None: + return + self.connection._i_am_closing(self) # take me off the connection's cursors list + try: + self.proxy.crsr_close(self.id) + except: + pass + try: + del self.description + except: + pass + try: + del self.rs # let go of the recordset + except: + pass + self.connection = ( + None # this will make all future method calls on me throw an exception + ) + self.proxy = None + if verbose: + print("adodbapi.remote Closed cursor at %X" % id(self)) + + def __del__(self): + try: + self.close() + except: + pass + + def setinputsizes(self, sizes): + pass + + def setoutputsize(self, size, column=None): + pass diff --git a/myenv/Lib/site-packages/adodbapi/schema_table.py b/myenv/Lib/site-packages/adodbapi/schema_table.py new file mode 100644 index 000000000..8621830e9 --- /dev/null +++ b/myenv/Lib/site-packages/adodbapi/schema_table.py @@ -0,0 +1,15 @@ +"""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/myenv/Lib/site-packages/adodbapi/setup.py b/myenv/Lib/site-packages/adodbapi/setup.py new file mode 100644 index 000000000..d25869adf --- /dev/null +++ b/myenv/Lib/site-packages/adodbapi/setup.py @@ -0,0 +1,70 @@ +"""adodbapi -- a pure Python PEP 249 DB-API package using Microsoft ADO + +Adodbapi can be run on CPython 3.5 and later. +or IronPython version 2.6 and later (in theory, possibly no longer in practice!) +""" +CLASSIFIERS = """\ +Development Status :: 5 - Production/Stable +Intended Audience :: Developers +License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL) +Operating System :: Microsoft :: Windows +Operating System :: POSIX :: Linux +Programming Language :: Python +Programming Language :: Python :: 3 +Programming Language :: SQL +Topic :: Software Development +Topic :: Software Development :: Libraries :: Python Modules +Topic :: Database +""" + +NAME = "adodbapi" +MAINTAINER = "Vernon Cole" +MAINTAINER_EMAIL = "vernondcole@gmail.com" +DESCRIPTION = ( + """A pure Python package implementing PEP 249 DB-API using Microsoft ADO.""" +) +URL = "http://sourceforge.net/projects/adodbapi" +LICENSE = "LGPL" +CLASSIFIERS = filter(None, CLASSIFIERS.split("\n")) +AUTHOR = "Henrik Ekelund, Vernon Cole, et.al." +AUTHOR_EMAIL = "vernondcole@gmail.com" +PLATFORMS = ["Windows", "Linux"] + +VERSION = None # in case searching for version fails +a = open("adodbapi.py") # find the version string in the source code +for line in a: + if "__version__" in line: + VERSION = line.split("'")[1] + print('adodbapi version="%s"' % VERSION) + break +a.close() + + +def setup_package(): + from distutils.command.build_py import build_py + from distutils.core import setup + + setup( + cmdclass={"build_py": build_py}, + name=NAME, + maintainer=MAINTAINER, + maintainer_email=MAINTAINER_EMAIL, + description=DESCRIPTION, + url=URL, + keywords="database ado odbc dbapi db-api Microsoft SQL", + ## download_url=DOWNLOAD_URL, + long_description=open("README.txt").read(), + license=LICENSE, + classifiers=CLASSIFIERS, + author=AUTHOR, + author_email=AUTHOR_EMAIL, + platforms=PLATFORMS, + version=VERSION, + package_dir={"adodbapi": ""}, + packages=["adodbapi"], + ) + return + + +if __name__ == "__main__": + setup_package() diff --git a/myenv/Lib/site-packages/adodbapi/test/adodbapitest.py b/myenv/Lib/site-packages/adodbapi/test/adodbapitest.py new file mode 100644 index 000000000..e5b3dc194 --- /dev/null +++ b/myenv/Lib/site-packages/adodbapi/test/adodbapitest.py @@ -0,0 +1,1692 @@ +""" Unit tests version 2.6.1.0 for adodbapi""" +""" + 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 copy +import datetime +import decimal +import random +import string +import sys +import unittest + +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 tryconnection + +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 + + +def str2bytes(sval): + return sval.encode("latin1") + + +long = int + + +def randomstring(length): + return "".join([random.choice(string.ascii_letters) for n in range(32)]) + + +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.assertEqual(row[0], "gabbagabba") + row = rows[1] + self.assertEqual(row[0], "heyhey") + self.assertEqual(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.assertEqual(row[0], "heyhey") # first will be unchanged + self.assertEqual(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.assertEqual(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) + 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.assertEqual(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.assertEqual( + 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) # remember the one that was prepared + 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 + ) # should execute this one, not the prepared one + 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 as an iterator, 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.assertEqual(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.assertEqual(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.assertEqual(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 # Asked 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, 'expected:"%s" but found "%s"' % (sel, crsr.command) + + # 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 fldID=:Id" % 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 fldID=%%(Id)s" % 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 + ppdcmd = ( + "insert into xx_%s (fldId,fldData) VALUES (:xyz,'six:five')" % config.tmp + ) + crsr.prepare(ppdcmd) + crsr.execute(ppdcmd, {"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() + + def testExtendedTypeHandling(self): + class XtendString(str): + pass + + class XtendInt(int): + pass + + class XtendFloat(float): + pass + + xs = XtendString(randomstring(30)) + xi = XtendInt(random.randint(-100, 500)) + xf = XtendFloat(random.random()) + self.helpForceDropOnTblTemp() + conn = self.getConnection() + crsr = conn.cursor() + tabdef = ( + """ + CREATE TABLE xx_%s ( + s VARCHAR(40) NOT NULL, + i INTEGER NOT NULL, + f REAL NOT NULL)""" + % config.tmp + ) + crsr.execute(tabdef) + crsr.execute( + "INSERT INTO xx_%s (s, i, f) VALUES (?, ?, ?)" % config.tmp, (xs, xi, xf) + ) + crsr.close() + conn = self.getConnection() + with self.getCursor() as crsr: + selectSql = "SELECT s, i, f from xx_%s" % config.tmp + crsr.execute( + selectSql + ) # closing the connection should _not_ have forced rollback + row = crsr.fetchone() + self.assertEqual(row.s, xs) + self.assertEqual(row.i, xi) + self.assertAlmostEqual(row.f, xf) + 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.assertEqual(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.assertEqual(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.assertEqual(comDate, correct) + + d = self.tc.Timestamp(2003, 5, 6, 14, 15, 17) + comDate = self.tc.COMDate(d) + correct = 37747.593946759262 + self.assertEqual(comDate, correct) + + def testIsoFormat(self): + d = self.tc.Timestamp(1994, 11, 15, 12, 3, 10) + iso = self.tc.DateObjectToIsoFormatString(d) + self.assertEqual(str(iso[:19]), "1994-11-15 12:03:10") + + dt = self.tc.Date(2003, 5, 2) + iso = self.tc.DateObjectToIsoFormatString(dt) + self.assertEqual(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 < cmd < t2, '"%s" should be about 2002-6-28 12:15:01' % repr(cmd) + + def testDate(self): + t1 = time.mktime((2002, 6, 28, 18, 15, 1, 4, 31 + 28 + 31 + 30 + 31 + 30, 0)) + t2 = time.mktime((2002, 6, 30, 18, 15, 1, 4, 31 + 28 + 31 + 30 + 31 + 28, 0)) + obj = self.tc.Date(2002, 6, 29) + assert t1 < time.mktime(obj) < t2, obj + + def testTime(self): + self.assertEqual( + self.tc.Time(18, 15, 2), time.gmtime(18 * 60 * 60 + 15 * 60 + 2) + ) + + def testTimestamp(self): + t1 = time.localtime( + time.mktime((2002, 6, 28, 18, 14, 1, 4, 31 + 28 + 31 + 30 + 31 + 28, -1)) + ) + t2 = time.localtime( + time.mktime((2002, 6, 28, 18, 16, 1, 4, 31 + 28 + 31 + 30 + 31 + 28, -1)) + ) + obj = self.tc.Timestamp(2002, 6, 28, 18, 15, 2) + assert t1 < obj < t2, obj + + +class TestPythonDateTimeConverter(TimeConverterInterfaceTest): + def setUp(self): + self.tc = api.pythonDateTimeConverter() + + def testCOMDate(self): + t = datetime.datetime(2002, 6, 28, 18, 15, 1) + # Fri, 28 Jun 2002 18:15:01 +0000 + cmd = self.tc.COMDate(t) + assert abs(cmd - 37435.7604282) < 1.0 / 24, "more than an hour wrong" + + def testDateObjectFromCOMDate(self): + cmd = self.tc.DateObjectFromCOMDate(37435.7604282) + t1 = datetime.datetime(2002, 6, 28, 18, 14, 1) + t2 = datetime.datetime(2002, 6, 28, 18, 16, 1) + assert t1 < cmd < t2, cmd + + tx = datetime.datetime( + 2002, 6, 28, 18, 14, 1, 900000 + ) # testing that microseconds don't become milliseconds + c1 = self.tc.DateObjectFromCOMDate(self.tc.COMDate(tx)) + assert t1 < c1 < t2, c1 + + def testDate(self): + t1 = datetime.date(2002, 6, 28) + t2 = datetime.date(2002, 6, 30) + obj = self.tc.Date(2002, 6, 29) + assert t1 < obj < t2, obj + + def testTime(self): + self.assertEqual(self.tc.Time(18, 15, 2).isoformat()[:8], "18:15:02") + + def testTimestamp(self): + t1 = datetime.datetime(2002, 6, 28, 18, 14, 1) + t2 = datetime.datetime(2002, 6, 28, 18, 16, 1) + obj = self.tc.Timestamp(2002, 6, 28, 18, 15, 2) + assert t1 < obj < t2, obj + + +suites = [] +suites.append(unittest.makeSuite(TestPythonDateTimeConverter, "test")) +if config.doMxDateTimeTest: + suites.append(unittest.makeSuite(TestMXDateTimeConverter, "test")) +if config.doTimeTest: + suites.append(unittest.makeSuite(TestPythonTimeConverter, "test")) + +if config.doAccessTest: + suites.append(unittest.makeSuite(TestADOwithAccessDB, "test")) +if config.doSqlServerTest: + suites.append(unittest.makeSuite(TestADOwithSQLServer, "test")) +if config.doMySqlTest: + suites.append(unittest.makeSuite(TestADOwithMySql, "test")) +if config.doPostgresTest: + suites.append(unittest.makeSuite(TestADOwithPostgres, "test")) + + +class cleanup_manager(object): + def __enter__(self): + pass + + def __exit__(self, exc_type, exc_val, exc_tb): + config.cleanup(config.testfolder, config.mdb_name) + + +suite = unittest.TestSuite(suites) +if __name__ == "__main__": + mysuite = copy.deepcopy(suite) + with cleanup_manager(): + defaultDateConverter = adodbapi.dateconverter + print(__doc__) + print("Default Date Converter is %s" % (defaultDateConverter,)) + dateconverter = defaultDateConverter + tag = "datetime" + unittest.TextTestRunner().run(mysuite) + + if config.iterateOverTimeTests: + for test, dateconverter, tag in ( + (config.doTimeTest, api.pythonTimeConverter, "pythontime"), + (config.doMxDateTimeTest, api.mxDateTimeConverter, "mx"), + ): + if test: + mysuite = copy.deepcopy( + suite + ) # work around a side effect of unittest.TextTestRunner + adodbapi.adodbapi.dateconverter = dateconverter() + print("Changed dateconverter to ") + print(adodbapi.adodbapi.dateconverter) + unittest.TextTestRunner().run(mysuite) diff --git a/myenv/Lib/site-packages/adodbapi/test/adodbapitestconfig.py b/myenv/Lib/site-packages/adodbapi/test/adodbapitestconfig.py new file mode 100644 index 000000000..98f254440 --- /dev/null +++ b/myenv/Lib/site-packages/adodbapi/test/adodbapitestconfig.py @@ -0,0 +1,221 @@ +# Configure this to _YOUR_ environment in order to run the testcases. +"testADOdbapiConfig.py v 2.6.2.B00" + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # +# # TESTERS: +# # +# # You will need to make numerous modifications to this file +# # to adapt it to your own testing environment. +# # +# # Skip down to the next "# #" line -- +# # -- the things you need to change are below it. +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +import platform +import random +import sys + +import is64bit +import setuptestframework +import tryconnection + +print("\nPython", sys.version) +node = platform.node() +try: + print( + "node=%s, is64bit.os()= %s, is64bit.Python()= %s" + % (node, is64bit.os(), is64bit.Python()) + ) +except: + pass + +if "--help" in sys.argv: + print( + """Valid command-line switches are: + --package - create a temporary test package, run 2to3 if needed. + --all - run all possible tests + --time - loop over time format tests (including mxdatetime if present) + --nojet - do not test against an ACCESS database file + --mssql - test against Microsoft SQL server + --pg - test against PostgreSQL + --mysql - test against MariaDB + --remote= - test unsing remote server at= (experimental) + """ + ) + exit() +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 = ( + "PYFGCRLAOEUIDHTNSQJKXBMWVZ" # why, yes, I do happen to use a dvorak keyboard +) +tmp = "".join([random.choice(_alphabet) for x in range(9)]) +mdb_name = "xx_" + tmp + ".mdb" # generate a non-colliding name for the temporary .mdb +testfolder = setuptestframework.maketemp() + +if "--package" in sys.argv: + # create a new adodbapi module -- running 2to3 if needed. + pth = setuptestframework.makeadopackage(testfolder) +else: + # use the adodbapi module in which this file appears + pth = setuptestframework.find_ado_path() +if pth not in sys.path: + # look here _first_ to find modules + sys.path.insert(1, pth) + +proxy_host = None +for arg in sys.argv: + if arg.startswith("--remote="): + proxy_host = arg.split("=")[1] + import adodbapi.remote as remote + + break + + +# function to clean up the temporary folder -- calling program must run this function before exit. +cleanup = setuptestframework.getcleanupfunction() +try: + import adodbapi # will (hopefully) be imported using the "pth" discovered above +except SyntaxError: + print( + '\n* * * Are you trying to run Python2 code using Python3? Re-run this test using the "--package" switch.' + ) + sys.exit(11) +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 + +doAllTests = "--all" in sys.argv +doAccessTest = not ("--nojet" in sys.argv) +doSqlServerTest = "--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 + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # start your environment setup here v v v +SQL_HOST_NODE = "testsql.2txt.us,1430" + +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 proxy_host: # determine the (probably remote) database file folder + c = {"macro_find_temp_test_path": ["mdb", mdb_name], "proxy_host": proxy_host} + else: + c = {"mdb": setuptestframework.makemdb(testfolder, mdb_name)} + + # macro definition for keyword "provider" using macro "is64bit" -- see documentation + # is64bit will return true for 64 bit versions of Python, so the macro will select the ACE provider + # (If running a remote ADO service, this will test the 64-bitedness of the ADO server.) + c["macro_is64bit"] = [ + "provider", + "Microsoft.ACE.OLEDB.12.0", # 64 bit provider + "Microsoft.Jet.OLEDB.4.0", + ] # 32 bit provider + connStrAccess = "Provider=%(provider)s;Data Source=%(mdb)s" # ;Mode=ReadWrite;Persist Security Info=False;Jet OLEDB:Bypass UserInfo Validation=True" + print( + " ...Testing ACCESS connection to {} file...".format( + c.get("mdb", "remote .mdb") + ) + ) + doAccessTest, connStrAccess, dbAccessconnect = tryconnection.try_connection( + verbose, connStrAccess, 10, **c + ) + +if doSqlServerTest: + c = { + "host": SQL_HOST_NODE, # name of computer with SQL Server + "database": "adotest", + "user": "adotestuser", # None implies Windows security + "password": "Sq1234567", + # macro definition for keyword "security" using macro "auto_security" + "macro_auto_security": "security", + "provider": "MSOLEDBSQL; MARS Connection=True", + } + if proxy_host: + c["proxy_host"] = proxy_host + connStr = "Provider=%(provider)s; Initial Catalog=%(database)s; Data Source=%(host)s; %(security)s;" + print(" ...Testing MS-SQL login to {}...".format(c["host"])) + ( + doSqlServerTest, + connStrSQLServer, + dbSqlServerconnect, + ) = tryconnection.try_connection(verbose, connStr, 30, **c) + +if doMySqlTest: + c = { + "host": "testmysql.2txt.us", + "database": "adodbapitest", + "user": "adotest", + "password": "12345678", + "port": "3330", # note the nonstandard port for obfuscation + "driver": "MySQL ODBC 5.1 Driver", + } # or _driver="MySQL ODBC 3.51 Driver + if proxy_host: + c["proxy_host"] = proxy_host + c["macro_is64bit"] = [ + "provider", + "Provider=MSDASQL;", + ] # turn on the 64 bit ODBC adapter only if needed + cs = ( + "%(provider)sDriver={%(driver)s};Server=%(host)s;Port=3330;" + + "Database=%(database)s;user=%(user)s;password=%(password)s;Option=3;" + ) + print(" ...Testing MySql login to {}...".format(c["host"])) + doMySqlTest, connStrMySql, dbMySqlconnect = tryconnection.try_connection( + verbose, cs, 5, **c + ) + + +if doPostgresTest: + _computername = "testpg.2txt.us" + _databasename = "adotest" + _username = "adotestuser" + _password = "12345678" + kws = {"timeout": 4} + kws["macro_is64bit"] = [ + "prov_drv", + "Provider=MSDASQL;Driver={PostgreSQL Unicode(x64)}", + "Driver=PostgreSQL Unicode", + ] + # get driver from http://www.postgresql.org/ftp/odbc/versions/ + # test using positional and keyword arguments (bad example for real code) + if proxy_host: + kws["proxy_host"] = proxy_host + print(" ...Testing PostgreSQL login to {}...".format(_computername)) + doPostgresTest, connStrPostgres, dbPostgresConnect = tryconnection.try_connection( + verbose, + "%(prov_drv)s;Server=%(host)s;Database=%(database)s;uid=%(user)s;pwd=%(password)s;port=5430;", # note nonstandard port + _username, + _password, + _computername, + _databasename, + **kws + ) + +assert ( + doAccessTest or doSqlServerTest or doMySqlTest or doPostgresTest +), "No database engine found for testing" diff --git a/myenv/Lib/site-packages/adodbapi/test/dbapi20.py b/myenv/Lib/site-packages/adodbapi/test/dbapi20.py new file mode 100644 index 000000000..e378b1941 --- /dev/null +++ b/myenv/Lib/site-packages/adodbapi/test/dbapi20.py @@ -0,0 +1,939 @@ +#!/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.15.0 $"[11:-2] +__author__ = "Stuart Bishop " + +import sys +import time +import unittest + +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 = False + +# Revision 1.15 2019/11/22 00:50:00 kf7xm +# Make Turn off IDEMPOTENT_CLOSE a proper skipTest + +# 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) + else: + self.skipTest( + "Non-idempotent close is considered a bad thing by some people." + ) + + 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/myenv/Lib/site-packages/adodbapi/test/is64bit.py b/myenv/Lib/site-packages/adodbapi/test/is64bit.py new file mode 100644 index 000000000..39834540d --- /dev/null +++ b/myenv/Lib/site-packages/adodbapi/test/is64bit.py @@ -0,0 +1,41 @@ +"""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/myenv/Lib/site-packages/adodbapi/test/setuptestframework.py b/myenv/Lib/site-packages/adodbapi/test/setuptestframework.py new file mode 100644 index 000000000..fcaaf1ae5 --- /dev/null +++ b/myenv/Lib/site-packages/adodbapi/test/setuptestframework.py @@ -0,0 +1,134 @@ +#!/usr/bin/python2 +# Configure this in order to run the testcases. +"setuptestframework.py v 2.6.0.8" +import os +import shutil +import sys +import tempfile + +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: + 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(testfolder) + print(" cleaned up folder", testfolder) + 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 os.path.isfile(_accessdatasource): + print("using JET database=", _accessdatasource) + else: + try: + from win32com.client import constants + from win32com.client.gencache import EnsureDispatch + + 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.dbVersion40 + ) + else: + newdb = dbe.CreateDatabase( + _accessdatasource, ";LANGID=0x0409;CP=1252;COUNTRY=0" + ) + newdb.Close() + else: + print(" ...copying test ACCESS db to " + _accessdatasource) + mdbName = os.path.abspath( + os.path.join(os.path.dirname(__file__), "..", "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/myenv/Lib/site-packages/adodbapi/test/test_adodbapi_dbapi20.py b/myenv/Lib/site-packages/adodbapi/test/test_adodbapi_dbapi20.py new file mode 100644 index 000000000..f8986484a --- /dev/null +++ b/myenv/Lib/site-packages/adodbapi/test/test_adodbapi_dbapi20.py @@ -0,0 +1,200 @@ +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 sys +import unittest + +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 = "testsql.2txt.us,1430" # if None, will use macro to fill in node name +instance = r"%s\SQLEXPRESS" +conn_kws["name"] = "adotest" + +conn_kws["user"] = "adotestuser" # None implies Windows security +conn_kws["password"] = "Sq1234567" +# macro definition for keyword "security" using macro "auto_security" +conn_kws["macro_auto_security"] = "security" + +if host is None: + conn_kws["macro_getnode"] = ["host", instance] +else: + conn_kws["host"] = host + +conn_kws[ + "provider" +] = "Provider=MSOLEDBSQL;DataTypeCompatibility=80;MARS Connection=True;" +connStr = "%(provider)s; %(security)s; 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 getTestMethodName(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.getTestMethodName() == "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.getTestMethodName() == "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/myenv/Lib/site-packages/adodbapi/test/tryconnection.py b/myenv/Lib/site-packages/adodbapi/test/tryconnection.py new file mode 100644 index 000000000..9d3901a8c --- /dev/null +++ b/myenv/Lib/site-packages/adodbapi/test/tryconnection.py @@ -0,0 +1,33 @@ +remote = False # automatic testing of remote access has been removed here + + +def try_connection(verbose, *args, **kwargs): + import adodbapi + + dbconnect = adodbapi.connect + 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)) + return False, (args, kwargs), None + + 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/myenv/Lib/site-packages/isapi/PyISAPI_loader.dll b/myenv/Lib/site-packages/isapi/PyISAPI_loader.dll new file mode 100644 index 000000000..a8316a26d Binary files /dev/null and b/myenv/Lib/site-packages/isapi/PyISAPI_loader.dll differ diff --git a/myenv/Lib/site-packages/isapi/README.txt b/myenv/Lib/site-packages/isapi/README.txt new file mode 100644 index 000000000..dc528624f --- /dev/null +++ b/myenv/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/myenv/Lib/site-packages/isapi/__init__.py b/myenv/Lib/site-packages/isapi/__init__.py new file mode 100644 index 000000000..71823616a --- /dev/null +++ b/myenv/Lib/site-packages/isapi/__init__.py @@ -0,0 +1,39 @@ +# 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/myenv/Lib/site-packages/isapi/doc/isapi.html b/myenv/Lib/site-packages/isapi/doc/isapi.html new file mode 100644 index 000000000..03001a1be --- /dev/null +++ b/myenv/Lib/site-packages/isapi/doc/isapi.html @@ -0,0 +1,92 @@ + + + +Introduction to Python ISAPI support + +

Introduction to Python ISAPI support

+ +

See also

+ +

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/myenv/Lib/site-packages/isapi/install.py b/myenv/Lib/site-packages/isapi/install.py new file mode 100644 index 000000000..154f82aff --- /dev/null +++ b/myenv/Lib/site-packages/isapi/install.py @@ -0,0 +1,815 @@ +"""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 imp +import os +import shutil +import stat +import sys +import traceback + +import pythoncom +import win32api +import winerror +from win32com.client import Dispatch, GetObject +from win32com.client.gencache import EnsureDispatch, EnsureModule + +_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/myenv/Lib/site-packages/isapi/isapicon.py b/myenv/Lib/site-packages/isapi/isapicon.py new file mode 100644 index 000000000..20de1a44c --- /dev/null +++ b/myenv/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/myenv/Lib/site-packages/isapi/samples/README.txt b/myenv/Lib/site-packages/isapi/samples/README.txt new file mode 100644 index 000000000..cff875873 --- /dev/null +++ b/myenv/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/myenv/Lib/site-packages/isapi/samples/advanced.py b/myenv/Lib/site-packages/isapi/samples/advanced.py new file mode 100644 index 000000000..c10d0c80c --- /dev/null +++ b/myenv/Lib/site-packages/isapi/samples/advanced.py @@ -0,0 +1,218 @@ +# 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. + +import os +import stat +import sys + +from isapi import isapicon +from isapi.simple import SimpleExtension + +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. + +import threading + +import win32con +import win32event +import win32file +import winerror + +from isapi import InternalReloadException + +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) + + qs = ecb.GetServerVariable("QUERY_STRING") + if qs: + queries = qs.split("&") + 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/myenv/Lib/site-packages/isapi/samples/redirector.py b/myenv/Lib/site-packages/isapi/samples/redirector.py new file mode 100644 index 000000000..40698bb2c --- /dev/null +++ b/myenv/Lib/site-packages/isapi/samples/redirector.py @@ -0,0 +1,125 @@ +# 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. + +import sys + +from isapi import isapicon, threaded_extension + +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/myenv/Lib/site-packages/isapi/samples/redirector_asynch.py b/myenv/Lib/site-packages/isapi/samples/redirector_asynch.py new file mode 100644 index 000000000..3c4b5e4f7 --- /dev/null +++ b/myenv/Lib/site-packages/isapi/samples/redirector_asynch.py @@ -0,0 +1,85 @@ +# 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!) + +import sys +import urllib.error +import urllib.parse +import urllib.request + +from isapi import isapicon, threaded_extension + +# 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/myenv/Lib/site-packages/isapi/samples/redirector_with_filter.py b/myenv/Lib/site-packages/isapi/samples/redirector_with_filter.py new file mode 100644 index 000000000..a63b1db13 --- /dev/null +++ b/myenv/Lib/site-packages/isapi/samples/redirector_with_filter.py @@ -0,0 +1,161 @@ +# 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. + +import sys +import urllib.error +import urllib.parse +import urllib.request + +from isapi import isapicon, threaded_extension +from isapi.simple import SimpleFilter + +# 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/myenv/Lib/site-packages/isapi/samples/test.py b/myenv/Lib/site-packages/isapi/samples/test.py new file mode 100644 index 000000000..5e4d899bb --- /dev/null +++ b/myenv/Lib/site-packages/isapi/samples/test.py @@ -0,0 +1,195 @@ +# 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. + +import os +import stat +import sys + +from isapi import isapicon +from isapi.simple import SimpleExtension + +if hasattr(sys, "isapidllhandle"): + import win32traceutil + +# We use the same reload support as 'advanced.py' demonstrates. +import threading + +import win32con +import win32event +import win32file +import winerror + +from isapi import InternalReloadException + + +# 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/myenv/Lib/site-packages/isapi/simple.py b/myenv/Lib/site-packages/isapi/simple.py new file mode 100644 index 000000000..b453bbae0 --- /dev/null +++ b/myenv/Lib/site-packages/isapi/simple.py @@ -0,0 +1,70 @@ +"""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/myenv/Lib/site-packages/isapi/test/README.txt b/myenv/Lib/site-packages/isapi/test/README.txt new file mode 100644 index 000000000..18643dd75 --- /dev/null +++ b/myenv/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/myenv/Lib/site-packages/isapi/test/extension_simple.py b/myenv/Lib/site-packages/isapi/test/extension_simple.py new file mode 100644 index 000000000..64bd71fd2 --- /dev/null +++ b/myenv/Lib/site-packages/isapi/test/extension_simple.py @@ -0,0 +1,119 @@ +# 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. + +import urllib.error +import urllib.parse +import urllib.request + +# 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 +import winerror + +from isapi import ExtensionError, isapicon, threaded_extension +from isapi.simple import SimpleFilter + +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/myenv/Lib/site-packages/isapi/threaded_extension.py b/myenv/Lib/site-packages/isapi/threaded_extension.py new file mode 100644 index 000000000..b31c8c9e7 --- /dev/null +++ b/myenv/Lib/site-packages/isapi/threaded_extension.py @@ -0,0 +1,189 @@ +"""An ISAPI extension base class implemented using a thread-pool.""" +# $Id$ + +import sys +import threading +import time +import traceback + +from pywintypes import OVERLAPPED +from win32event import INFINITE +from win32file import ( + CloseHandle, + CreateIoCompletionPort, + GetQueuedCompletionStatus, + PostQueuedCompletionStatus, +) +from win32security import SetThreadToken + +import isapi.simple +from isapi import ExtensionError, isapicon + +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.is_alive()] + 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("

Traceback (most recent call last):

", file=ecb) + list = traceback.format_tb( + exc_tb, limit + ) + traceback.format_exception_only(exc_typ, exc_val) + print( + "
%s%s
" + % ( + cgi.escape("".join(list[:-1])), + cgi.escape(list[-1]), + ), + file=ecb, + ) + except ExtensionError: + # The client disconnected without reading the error body - + # its probably not a real browser at the other end, ignore it. + pass + except: + print("FAILED to render the error message!") + traceback.print_exc() + print("ORIGINAL extension error:") + traceback.print_exception(exc_typ, exc_val, exc_tb) + finally: + # holding tracebacks in a local of a frame that may itself be + # part of a traceback used to be evil and cause leaks! + exc_tb = None + ecb.DoneWithSession() diff --git a/myenv/Lib/site-packages/pythoncom.py b/myenv/Lib/site-packages/pythoncom.py new file mode 100644 index 000000000..2180ecc32 --- /dev/null +++ b/myenv/Lib/site-packages/pythoncom.py @@ -0,0 +1,4 @@ +# Magic utility that "redirects" to pythoncomxx.dll +import pywintypes + +pywintypes.__import_pywin32_system_module__("pythoncom", globals()) diff --git a/myenv/Lib/site-packages/pythonwin/Pythonwin.exe b/myenv/Lib/site-packages/pythonwin/Pythonwin.exe new file mode 100644 index 000000000..14d9a28eb Binary files /dev/null and b/myenv/Lib/site-packages/pythonwin/Pythonwin.exe differ diff --git a/myenv/Lib/site-packages/pythonwin/dde.pyd b/myenv/Lib/site-packages/pythonwin/dde.pyd new file mode 100644 index 000000000..6f1a77b54 Binary files /dev/null and b/myenv/Lib/site-packages/pythonwin/dde.pyd differ diff --git a/myenv/Lib/site-packages/pythonwin/license.txt b/myenv/Lib/site-packages/pythonwin/license.txt new file mode 100644 index 000000000..fa340d745 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/license.txt @@ -0,0 +1,30 @@ +Unless stated in the specfic source file, this work is +Copyright (c) 1994-2008, 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 name of 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/myenv/Lib/site-packages/pythonwin/mfc140u.dll b/myenv/Lib/site-packages/pythonwin/mfc140u.dll new file mode 100644 index 000000000..affd071ed Binary files /dev/null and b/myenv/Lib/site-packages/pythonwin/mfc140u.dll differ diff --git a/myenv/Lib/site-packages/pythonwin/pywin/Demos/app/basictimerapp.py b/myenv/Lib/site-packages/pythonwin/pywin/Demos/app/basictimerapp.py new file mode 100644 index 000000000..da771da11 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/Demos/app/basictimerapp.py @@ -0,0 +1,258 @@ +# basictimerapp - a really simple timer application. +# This should be run using the command line: +# pythonwin /app demos\basictimerapp.py +import sys +import time + +import timer +import win32api +import win32con +import win32ui +from pywin.framework import app, cmdline, dlgappcore + + +class TimerAppDialog(dlgappcore.AppDialog): + softspace = 1 + + def __init__(self, appName=""): + dlgappcore.AppDialog.__init__(self, win32ui.IDD_GENERAL_STATUS) + self.timerAppName = appName + self.argOff = 0 + if len(self.timerAppName) == 0: + if len(sys.argv) > 1 and sys.argv[1][0] != "/": + self.timerAppName = sys.argv[1] + self.argOff = 1 + + def PreDoModal(self): + # sys.stderr = sys.stdout + pass + + def ProcessArgs(self, args): + for arg in args: + if arg == "/now": + self.OnOK() + + def OnInitDialog(self): + win32ui.SetProfileFileName("pytimer.ini") + self.title = win32ui.GetProfileVal( + self.timerAppName, "Title", "Remote System Timer" + ) + self.buildTimer = win32ui.GetProfileVal( + self.timerAppName, "Timer", "EachMinuteIntervaler()" + ) + self.doWork = win32ui.GetProfileVal(self.timerAppName, "Work", "DoDemoWork()") + # replace "\n" with real \n. + self.doWork = self.doWork.replace("\\n", "\n") + dlgappcore.AppDialog.OnInitDialog(self) + + self.SetWindowText(self.title) + self.prompt1 = self.GetDlgItem(win32ui.IDC_PROMPT1) + self.prompt2 = self.GetDlgItem(win32ui.IDC_PROMPT2) + self.prompt3 = self.GetDlgItem(win32ui.IDC_PROMPT3) + self.butOK = self.GetDlgItem(win32con.IDOK) + self.butCancel = self.GetDlgItem(win32con.IDCANCEL) + self.prompt1.SetWindowText("Python Timer App") + self.prompt2.SetWindowText("") + self.prompt3.SetWindowText("") + self.butOK.SetWindowText("Do it now") + self.butCancel.SetWindowText("Close") + + self.timerManager = TimerManager(self) + self.ProcessArgs(sys.argv[self.argOff :]) + self.timerManager.go() + return 1 + + def OnDestroy(self, msg): + dlgappcore.AppDialog.OnDestroy(self, msg) + self.timerManager.stop() + + def OnOK(self): + # stop the timer, then restart after setting special boolean + self.timerManager.stop() + self.timerManager.bConnectNow = 1 + self.timerManager.go() + return + + +# def OnCancel(self): default behaviour - cancel == close. +# return + + +class TimerManager: + def __init__(self, dlg): + self.dlg = dlg + self.timerId = None + self.intervaler = eval(self.dlg.buildTimer) + self.bConnectNow = 0 + self.bHaveSetPrompt1 = 0 + + def CaptureOutput(self): + self.oldOut = sys.stdout + self.oldErr = sys.stderr + sys.stdout = sys.stderr = self + self.bHaveSetPrompt1 = 0 + + def ReleaseOutput(self): + sys.stdout = self.oldOut + sys.stderr = self.oldErr + + def write(self, str): + s = str.strip() + if len(s): + if self.bHaveSetPrompt1: + dest = self.dlg.prompt3 + else: + dest = self.dlg.prompt1 + self.bHaveSetPrompt1 = 1 + dest.SetWindowText(s) + + def go(self): + self.OnTimer(None, None) + + def stop(self): + if self.timerId: + timer.kill_timer(self.timerId) + self.timerId = None + + def OnTimer(self, id, timeVal): + if id: + timer.kill_timer(id) + if self.intervaler.IsTime() or self.bConnectNow: + # do the work. + try: + self.dlg.SetWindowText(self.dlg.title + " - Working...") + self.dlg.butOK.EnableWindow(0) + self.dlg.butCancel.EnableWindow(0) + self.CaptureOutput() + try: + exec(self.dlg.doWork) + print("The last operation completed successfully.") + except: + t, v, tb = sys.exc_info() + str = "Failed: %s: %s" % (t, repr(v)) + print(str) + self.oldErr.write(str) + tb = None # Prevent cycle + finally: + self.ReleaseOutput() + self.dlg.butOK.EnableWindow() + self.dlg.butCancel.EnableWindow() + self.dlg.SetWindowText(self.dlg.title) + else: + now = time.time() + nextTime = self.intervaler.GetNextTime() + if nextTime: + timeDiffSeconds = nextTime - now + timeDiffMinutes = int(timeDiffSeconds / 60) + timeDiffSeconds = timeDiffSeconds % 60 + timeDiffHours = int(timeDiffMinutes / 60) + timeDiffMinutes = timeDiffMinutes % 60 + self.dlg.prompt1.SetWindowText( + "Next connection due in %02d:%02d:%02d" + % (timeDiffHours, timeDiffMinutes, timeDiffSeconds) + ) + self.timerId = timer.set_timer( + self.intervaler.GetWakeupInterval(), self.OnTimer + ) + self.bConnectNow = 0 + + +class TimerIntervaler: + def __init__(self): + self.nextTime = None + self.wakeUpInterval = 2000 + + def GetWakeupInterval(self): + return self.wakeUpInterval + + def GetNextTime(self): + return self.nextTime + + def IsTime(self): + now = time.time() + if self.nextTime is None: + self.nextTime = self.SetFirstTime(now) + ret = 0 + if now >= self.nextTime: + ret = 1 + self.nextTime = self.SetNextTime(self.nextTime, now) + # do the work. + return ret + + +class EachAnyIntervaler(TimerIntervaler): + def __init__(self, timeAt, timePos, timeAdd, wakeUpInterval=None): + TimerIntervaler.__init__(self) + self.timeAt = timeAt + self.timePos = timePos + self.timeAdd = timeAdd + if wakeUpInterval: + self.wakeUpInterval = wakeUpInterval + + def SetFirstTime(self, now): + timeTup = time.localtime(now) + lst = [] + for item in timeTup: + lst.append(item) + bAdd = timeTup[self.timePos] > self.timeAt + lst[self.timePos] = self.timeAt + for pos in range(self.timePos + 1, 6): + lst[pos] = 0 + ret = time.mktime(tuple(lst)) + if bAdd: + ret = ret + self.timeAdd + return ret + + def SetNextTime(self, lastTime, now): + return lastTime + self.timeAdd + + +class EachMinuteIntervaler(EachAnyIntervaler): + def __init__(self, at=0): + EachAnyIntervaler.__init__(self, at, 5, 60, 2000) + + +class EachHourIntervaler(EachAnyIntervaler): + def __init__(self, at=0): + EachAnyIntervaler.__init__(self, at, 4, 3600, 10000) + + +class EachDayIntervaler(EachAnyIntervaler): + def __init__(self, at=0): + EachAnyIntervaler.__init__(self, at, 3, 86400, 10000) + + +class TimerDialogApp(dlgappcore.DialogApp): + def CreateDialog(self): + return TimerAppDialog() + + +def DoDemoWork(): + print("Doing the work...") + print("About to connect") + win32api.MessageBeep(win32con.MB_ICONASTERISK) + win32api.Sleep(2000) + print("Doing something else...") + win32api.MessageBeep(win32con.MB_ICONEXCLAMATION) + win32api.Sleep(2000) + print("More work.") + win32api.MessageBeep(win32con.MB_ICONHAND) + win32api.Sleep(2000) + print("The last bit.") + win32api.MessageBeep(win32con.MB_OK) + win32api.Sleep(2000) + + +app = TimerDialogApp() + + +def t(): + t = TimerAppDialog("Test Dialog") + t.DoModal() + return t + + +if __name__ == "__main__": + import demoutils + + demoutils.NeedApp() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/Demos/app/customprint.py b/myenv/Lib/site-packages/pythonwin/pywin/Demos/app/customprint.py new file mode 100644 index 000000000..356073e5f --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/Demos/app/customprint.py @@ -0,0 +1,186 @@ +# A demo of an Application object that has some custom print functionality. + +# If you desire, you can also run this from inside Pythonwin, in which +# case it will do the demo inside the Pythonwin environment. + +# This sample was contributed by Roger Burnham. + +import win32api +import win32con +import win32ui +from pywin.framework import app +from pywin.mfc import afxres, dialog, docview + +PRINTDLGORD = 1538 +IDC_PRINT_MAG_EDIT = 1010 + + +class PrintDemoTemplate(docview.DocTemplate): + def _SetupSharedMenu_(self): + pass + + +class PrintDemoView(docview.ScrollView): + def OnInitialUpdate(self): + ret = self._obj_.OnInitialUpdate() + self.colors = { + "Black": (0x00 << 0) + (0x00 << 8) + (0x00 << 16), + "Red": (0xFF << 0) + (0x00 << 8) + (0x00 << 16), + "Green": (0x00 << 0) + (0xFF << 8) + (0x00 << 16), + "Blue": (0x00 << 0) + (0x00 << 8) + (0xFF << 16), + "Cyan": (0x00 << 0) + (0xFF << 8) + (0xFF << 16), + "Magenta": (0xFF << 0) + (0x00 << 8) + (0xFF << 16), + "Yellow": (0xFF << 0) + (0xFF << 8) + (0x00 << 16), + } + self.pens = {} + for name, color in self.colors.items(): + self.pens[name] = win32ui.CreatePen(win32con.PS_SOLID, 5, color) + self.pen = None + self.size = (128, 128) + self.SetScaleToFitSize(self.size) + self.HookCommand(self.OnFilePrint, afxres.ID_FILE_PRINT) + self.HookCommand(self.OnFilePrintPreview, win32ui.ID_FILE_PRINT_PREVIEW) + return ret + + def OnDraw(self, dc): + oldPen = None + x, y = self.size + delta = 2 + colors = list(self.colors.keys()) + colors.sort() + colors = colors * 2 + for color in colors: + if oldPen is None: + oldPen = dc.SelectObject(self.pens[color]) + else: + dc.SelectObject(self.pens[color]) + dc.MoveTo((delta, delta)) + dc.LineTo((x - delta, delta)) + dc.LineTo((x - delta, y - delta)) + dc.LineTo((delta, y - delta)) + dc.LineTo((delta, delta)) + delta = delta + 4 + if x - delta <= 0 or y - delta <= 0: + break + dc.SelectObject(oldPen) + + def OnPrepareDC(self, dc, pInfo): + if dc.IsPrinting(): + mag = self.prtDlg["mag"] + dc.SetMapMode(win32con.MM_ANISOTROPIC) + dc.SetWindowOrg((0, 0)) + dc.SetWindowExt((1, 1)) + dc.SetViewportOrg((0, 0)) + dc.SetViewportExt((mag, mag)) + + def OnPreparePrinting(self, pInfo): + flags = ( + win32ui.PD_USEDEVMODECOPIES + | win32ui.PD_PAGENUMS + | win32ui.PD_NOPAGENUMS + | win32ui.PD_NOSELECTION + ) + self.prtDlg = ImagePrintDialog(pInfo, PRINTDLGORD, flags) + pInfo.SetPrintDialog(self.prtDlg) + pInfo.SetMinPage(1) + pInfo.SetMaxPage(1) + pInfo.SetFromPage(1) + pInfo.SetToPage(1) + ret = self.DoPreparePrinting(pInfo) + return ret + + def OnBeginPrinting(self, dc, pInfo): + return self._obj_.OnBeginPrinting(dc, pInfo) + + def OnEndPrinting(self, dc, pInfo): + del self.prtDlg + return self._obj_.OnEndPrinting(dc, pInfo) + + def OnFilePrintPreview(self, *arg): + self._obj_.OnFilePrintPreview() + + def OnFilePrint(self, *arg): + self._obj_.OnFilePrint() + + def OnPrint(self, dc, pInfo): + doc = self.GetDocument() + metrics = dc.GetTextMetrics() + cxChar = metrics["tmAveCharWidth"] + cyChar = metrics["tmHeight"] + left, top, right, bottom = pInfo.GetDraw() + dc.TextOut(0, 2 * cyChar, doc.GetTitle()) + top = top + (7 * cyChar) / 2 + dc.MoveTo(left, top) + dc.LineTo(right, top) + top = top + cyChar + # this seems to have not effect... + # get what I want with the dc.SetWindowOrg calls + pInfo.SetDraw((left, top, right, bottom)) + dc.SetWindowOrg((0, -top)) + + self.OnDraw(dc) + dc.SetTextAlign(win32con.TA_LEFT | win32con.TA_BOTTOM) + + rect = self.GetWindowRect() + rect = self.ScreenToClient(rect) + height = rect[3] - rect[1] + dc.SetWindowOrg((0, -(top + height + cyChar))) + dc.MoveTo(left, 0) + dc.LineTo(right, 0) + + x = 0 + y = (3 * cyChar) / 2 + + dc.TextOut(x, y, doc.GetTitle()) + y = y + cyChar + + +class PrintDemoApp(app.CApp): + def __init__(self): + app.CApp.__init__(self) + + def InitInstance(self): + template = PrintDemoTemplate(None, None, None, PrintDemoView) + self.AddDocTemplate(template) + self._obj_.InitMDIInstance() + self.LoadMainFrame() + doc = template.OpenDocumentFile(None) + doc.SetTitle("Custom Print Document") + + +class ImagePrintDialog(dialog.PrintDialog): + sectionPos = "Image Print Demo" + + def __init__(self, pInfo, dlgID, flags=win32ui.PD_USEDEVMODECOPIES): + dialog.PrintDialog.__init__(self, pInfo, dlgID, flags=flags) + mag = win32ui.GetProfileVal(self.sectionPos, "Document Magnification", 0) + if mag <= 0: + mag = 2 + win32ui.WriteProfileVal(self.sectionPos, "Document Magnification", mag) + + self["mag"] = mag + + def OnInitDialog(self): + self.magCtl = self.GetDlgItem(IDC_PRINT_MAG_EDIT) + self.magCtl.SetWindowText(repr(self["mag"])) + return dialog.PrintDialog.OnInitDialog(self) + + def OnOK(self): + dialog.PrintDialog.OnOK(self) + strMag = self.magCtl.GetWindowText() + try: + self["mag"] = int(strMag) + except: + pass + win32ui.WriteProfileVal(self.sectionPos, "Document Magnification", self["mag"]) + + +if __name__ == "__main__": + # Running under Pythonwin + def test(): + template = PrintDemoTemplate(None, None, None, PrintDemoView) + template.OpenDocumentFile(None) + + test() +else: + app = PrintDemoApp() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/Demos/app/demoutils.py b/myenv/Lib/site-packages/pythonwin/pywin/Demos/app/demoutils.py new file mode 100644 index 000000000..ee1fefcfa --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/Demos/app/demoutils.py @@ -0,0 +1,65 @@ +# Utilities for the demos + +import sys + +import win32api +import win32con +import win32ui + +NotScriptMsg = """\ +This demo program is not designed to be run as a Script, but is +probably used by some other test program. Please try another demo. +""" + +NeedGUIMsg = """\ +This demo program can only be run from inside of Pythonwin + +You must start Pythonwin, and select 'Run' from the toolbar or File menu +""" + + +NeedAppMsg = """\ +This demo program is a 'Pythonwin Application'. + +It is more demo code than an example of Pythonwin's capabilities. + +To run it, you must execute the command: +pythonwin.exe /app "%s" + +Would you like to execute it now? +""" + + +def NotAScript(): + import win32ui + + win32ui.MessageBox(NotScriptMsg, "Demos") + + +def NeedGoodGUI(): + from pywin.framework.app import HaveGoodGUI + + rc = HaveGoodGUI() + if not rc: + win32ui.MessageBox(NeedGUIMsg, "Demos") + return rc + + +def NeedApp(): + import win32ui + + rc = win32ui.MessageBox(NeedAppMsg % sys.argv[0], "Demos", win32con.MB_YESNO) + if rc == win32con.IDYES: + try: + parent = win32ui.GetMainFrame().GetSafeHwnd() + win32api.ShellExecute( + parent, None, "pythonwin.exe", '/app "%s"' % sys.argv[0], None, 1 + ) + except win32api.error as details: + win32ui.MessageBox("Error executing command - %s" % (details), "Demos") + + +if __name__ == "__main__": + import demoutils + + demoutils.NotAScript() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/Demos/app/dlgappdemo.py b/myenv/Lib/site-packages/pythonwin/pywin/Demos/app/dlgappdemo.py new file mode 100644 index 000000000..38659a62f --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/Demos/app/dlgappdemo.py @@ -0,0 +1,51 @@ +# dlgappdemo - a demo of a dialog application. +# This is a demonstration of both a custom "application" module, +# and a Python program in a dialog box. +# +# NOTE: You CAN NOT import this module from either PythonWin or Python. +# This module must be specified on the commandline to PythonWin only. +# eg, PythonWin /app dlgappdemo.py + +import sys + +import win32ui +from pywin.framework import app, dlgappcore + + +class TestDialogApp(dlgappcore.DialogApp): + def CreateDialog(self): + return TestAppDialog() + + +class TestAppDialog(dlgappcore.AppDialog): + def __init__(self): + self.edit = None + dlgappcore.AppDialog.__init__(self, win32ui.IDD_LARGE_EDIT) + + def OnInitDialog(self): + self.SetWindowText("Test dialog application") + self.edit = self.GetDlgItem(win32ui.IDC_EDIT1) + print("Hello from Python") + print("args are:", end=" ") + for arg in sys.argv: + print(arg) + return 1 + + def PreDoModal(self): + sys.stdout = sys.stderr = self + + def write(self, str): + if self.edit: + self.edit.SetSel(-2) + # translate \n to \n\r + self.edit.ReplaceSel(str.replace("\n", "\r\n")) + else: + win32ui.OutputDebug("dlgapp - no edit control! >>\n%s\n<<\n" % str) + + +app.AppBuilder = TestDialogApp + +if __name__ == "__main__": + import demoutils + + demoutils.NeedApp() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/Demos/app/dojobapp.py b/myenv/Lib/site-packages/pythonwin/pywin/Demos/app/dojobapp.py new file mode 100644 index 000000000..d97ce5e2a --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/Demos/app/dojobapp.py @@ -0,0 +1,72 @@ +# dojobapp - do a job, show the result in a dialog, and exit. +# +# Very simple - faily minimal dialog based app. +# +# This should be run using the command line: +# pythonwin /app demos\dojobapp.py + + +import win32api +import win32con +import win32ui +from pywin.framework import app, dlgappcore + + +class DoJobAppDialog(dlgappcore.AppDialog): + softspace = 1 + + def __init__(self, appName=""): + self.appName = appName + dlgappcore.AppDialog.__init__(self, win32ui.IDD_GENERAL_STATUS) + + def PreDoModal(self): + pass + + def ProcessArgs(self, args): + pass + + def OnInitDialog(self): + self.SetWindowText(self.appName) + butCancel = self.GetDlgItem(win32con.IDCANCEL) + butCancel.ShowWindow(win32con.SW_HIDE) + p1 = self.GetDlgItem(win32ui.IDC_PROMPT1) + p2 = self.GetDlgItem(win32ui.IDC_PROMPT2) + + # Do something here! + + p1.SetWindowText("Hello there") + p2.SetWindowText("from the demo") + + def OnDestroy(self, msg): + pass + + +# def OnOK(self): +# pass +# def OnCancel(self): default behaviour - cancel == close. +# return + + +class DoJobDialogApp(dlgappcore.DialogApp): + def CreateDialog(self): + return DoJobAppDialog("Do Something") + + +class CopyToDialogApp(DoJobDialogApp): + def __init__(self): + DoJobDialogApp.__init__(self) + + +app.AppBuilder = DoJobDialogApp + + +def t(): + t = DoJobAppDialog("Copy To") + t.DoModal() + return t + + +if __name__ == "__main__": + import demoutils + + demoutils.NeedApp() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/Demos/app/helloapp.py b/myenv/Lib/site-packages/pythonwin/pywin/Demos/app/helloapp.py new file mode 100644 index 000000000..876f16f71 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/Demos/app/helloapp.py @@ -0,0 +1,53 @@ +## +## helloapp.py +## +## +## A nice, small 'hello world' Pythonwin application. +## NOT an MDI application - just a single, normal, top-level window. +## +## MUST be run with the command line "pythonwin.exe /app helloapp.py" +## (or if you are really keen, rename "pythonwin.exe" to something else, then +## using MSVC or similar, edit the string section in the .EXE to name this file) +## +## Originally by Willy Heineman + + +import win32con +import win32ui +from pywin.mfc import afxres, dialog, window +from pywin.mfc.thread import WinApp + + +# The main frame. +# Does almost nothing at all - doesnt even create a child window! +class HelloWindow(window.Wnd): + def __init__(self): + # The window.Wnd ctor creates a Window object, and places it in + # self._obj_. Note the window object exists, but the window itself + # does not! + window.Wnd.__init__(self, win32ui.CreateWnd()) + + # Now we ask the window object to create the window itself. + self._obj_.CreateWindowEx( + win32con.WS_EX_CLIENTEDGE, + win32ui.RegisterWndClass(0, 0, win32con.COLOR_WINDOW + 1), + "Hello World!", + win32con.WS_OVERLAPPEDWINDOW, + (100, 100, 400, 300), + None, + 0, + None, + ) + + +# The application object itself. +class HelloApp(WinApp): + def InitInstance(self): + self.frame = HelloWindow() + self.frame.ShowWindow(win32con.SW_SHOWNORMAL) + # We need to tell MFC what our main frame is. + self.SetMainFrame(self.frame) + + +# Now create the application object itself! +app = HelloApp() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/Demos/cmdserver.py b/myenv/Lib/site-packages/pythonwin/pywin/Demos/cmdserver.py new file mode 100644 index 000000000..7b1c257a3 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/Demos/cmdserver.py @@ -0,0 +1,116 @@ +# cmdserver.py + +# Demo code that is not Pythonwin related, but too good to throw away... + +import _thread +import sys +import traceback + +import win32api +from pywin.framework import winout + + +class ThreadWriter: + "Assign an instance to sys.stdout for per-thread printing objects - Courtesy Guido!" + + def __init__(self): + "Constructor -- initialize the table of writers" + self.writers = {} + self.origStdOut = None + + def register(self, writer): + "Register the writer for the current thread" + self.writers[_thread.get_ident()] = writer + if self.origStdOut is None: + self.origStdOut = sys.stdout + sys.stdout = self + + def unregister(self): + "Remove the writer for the current thread, if any" + try: + del self.writers[_thread.get_ident()] + except KeyError: + pass + if len(self.writers) == 0: + sys.stdout = self.origStdOut + self.origStdOut = None + + def getwriter(self): + "Return the current thread's writer, default sys.stdout" + try: + return self.writers[_thread.get_ident()] + except KeyError: + return self.origStdOut + + def write(self, str): + "Write to the current thread's writer, default sys.stdout" + self.getwriter().write(str) + + +def Test(): + num = 1 + while num < 1000: + print("Hello there no " + str(num)) + win32api.Sleep(50) + num = num + 1 + + +class flags: + SERVER_BEST = 0 + SERVER_IMMEDIATE = 1 + SERVER_THREAD = 2 + SERVER_PROCESS = 3 + + +def StartServer(cmd, title=None, bCloseOnEnd=0, serverFlags=flags.SERVER_BEST): + out = winout.WindowOutput(title, None, winout.flags.WQ_IDLE) + if not title: + title = cmd + out.Create(title) + # ServerThread((out, cmd, title, bCloseOnEnd)) + # out = sys.stdout + _thread.start_new_thread(ServerThread, (out, cmd, title, bCloseOnEnd)) + + +def ServerThread(myout, cmd, title, bCloseOnEnd): + try: + writer.register(myout) + print('Executing "%s"\n' % cmd) + bOK = 1 + try: + import __main__ + + exec(cmd + "\n", __main__.__dict__) + except: + bOK = 0 + if bOK: + print("Command terminated without errors.") + else: + t, v, tb = sys.exc_info() + print(t, ": ", v) + traceback.print_tb(tb) + tb = None # prevent a cycle + print("Command terminated with an unhandled exception") + writer.unregister() + if bOK and bCloseOnEnd: + myout.frame.DestroyWindow() + + # Unhandled exception of any kind in a thread kills the gui! + except: + t, v, tb = sys.exc_info() + print(t, ": ", v) + traceback.print_tb(tb) + tb = None + print("Thread failed") + + +# assist for reloading (when debugging) - use only 1 tracer object, +# else a large chain of tracer objects will exist. +# try: +# writer +# except NameError: +# writer=ThreadWriter() +if __name__ == "__main__": + import demoutils + + demoutils.NotAScript() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/Demos/createwin.py b/myenv/Lib/site-packages/pythonwin/pywin/Demos/createwin.py new file mode 100644 index 000000000..38f6947ed --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/Demos/createwin.py @@ -0,0 +1,114 @@ +# +# Window creation example +# +# This example creates a minimal "control" that just fills in its +# window with red. To make your own control, subclass Control and +# write your own OnPaint() method. See PyCWnd.HookMessage for what +# the parameters to OnPaint are. +# + +import win32api +import win32con +import win32ui +from pywin.mfc import dialog, window + + +class Control(window.Wnd): + """Generic control class""" + + def __init__(self): + window.Wnd.__init__(self, win32ui.CreateWnd()) + + def OnPaint(self): + dc, paintStruct = self.BeginPaint() + self.DoPaint(dc) + self.EndPaint(paintStruct) + + def DoPaint(self, dc): # Override this! + pass + + +class RedBox(Control): + def DoPaint(self, dc): + dc.FillSolidRect(self.GetClientRect(), win32api.RGB(255, 0, 0)) + + +class RedBoxWithPie(RedBox): + def DoPaint(self, dc): + RedBox.DoPaint(self, dc) + r = self.GetClientRect() + dc.Pie(r[0], r[1], r[2], r[3], 0, 0, r[2], r[3] // 2) + + +def MakeDlgTemplate(): + style = ( + win32con.DS_MODALFRAME + | win32con.WS_POPUP + | win32con.WS_VISIBLE + | win32con.WS_CAPTION + | win32con.WS_SYSMENU + | win32con.DS_SETFONT + ) + cs = win32con.WS_CHILD | win32con.WS_VISIBLE + + w = 64 + h = 64 + + dlg = [ + ["Red box", (0, 0, w, h), style, None, (8, "MS Sans Serif")], + ] + + s = win32con.WS_TABSTOP | cs + + dlg.append( + [ + 128, + "Cancel", + win32con.IDCANCEL, + (7, h - 18, 50, 14), + s | win32con.BS_PUSHBUTTON, + ] + ) + + return dlg + + +class TestDialog(dialog.Dialog): + def OnInitDialog(self): + rc = dialog.Dialog.OnInitDialog(self) + self.redbox = RedBox() + self.redbox.CreateWindow( + None, + "RedBox", + win32con.WS_CHILD | win32con.WS_VISIBLE, + (5, 5, 90, 68), + self, + 1003, + ) + return rc + + +class TestPieDialog(dialog.Dialog): + def OnInitDialog(self): + rc = dialog.Dialog.OnInitDialog(self) + self.control = RedBoxWithPie() + self.control.CreateWindow( + None, + "RedBox with Pie", + win32con.WS_CHILD | win32con.WS_VISIBLE, + (5, 5, 90, 68), + self, + 1003, + ) + + +def demo(modal=0): + d = TestPieDialog(MakeDlgTemplate()) + if modal: + d.DoModal() + else: + d.CreateWindow() + + +if __name__ == "__main__": + demo(1) diff --git a/myenv/Lib/site-packages/pythonwin/pywin/Demos/demoutils.py b/myenv/Lib/site-packages/pythonwin/pywin/Demos/demoutils.py new file mode 100644 index 000000000..f6080c465 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/Demos/demoutils.py @@ -0,0 +1,67 @@ +# Utilities for the demos + +import sys + +import win32api +import win32con +import win32ui + +NotScriptMsg = """\ +This demo program is not designed to be run as a Script, but is +probably used by some other test program. Please try another demo. +""" + +NeedGUIMsg = """\ +This demo program can only be run from inside of Pythonwin + +You must start Pythonwin, and select 'Run' from the toolbar or File menu +""" + + +NeedAppMsg = """\ +This demo program is a 'Pythonwin Application'. + +It is more demo code than an example of Pythonwin's capabilities. + +To run it, you must execute the command: +pythonwin.exe /app "%s" + +Would you like to execute it now? +""" + + +def NotAScript(): + import win32ui + + win32ui.MessageBox(NotScriptMsg, "Demos") + + +def NeedGoodGUI(): + from pywin.framework.app import HaveGoodGUI + + rc = HaveGoodGUI() + if not rc: + win32ui.MessageBox(NeedGUIMsg, "Demos") + return rc + + +def NeedApp(): + import win32ui + + rc = win32ui.MessageBox(NeedAppMsg % sys.argv[0], "Demos", win32con.MB_YESNO) + if rc == win32con.IDYES: + try: + parent = win32ui.GetMainFrame().GetSafeHwnd() + win32api.ShellExecute( + parent, None, "pythonwin.exe", '/app "%s"' % sys.argv[0], None, 1 + ) + except win32api.error as details: + win32ui.MessageBox("Error executing command - %s" % (details), "Demos") + + +from pywin.framework.app import HaveGoodGUI + +if __name__ == "__main__": + import demoutils + + demoutils.NotAScript() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/Demos/dibdemo.py b/myenv/Lib/site-packages/pythonwin/pywin/Demos/dibdemo.py new file mode 100644 index 000000000..615227abf --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/Demos/dibdemo.py @@ -0,0 +1,73 @@ +# A demo which creates a view and a frame which displays a PPM format bitmap +# +# This hasnnt been run in a while, as I dont have many of that format around! + +import win32api +import win32con +import win32ui + + +class DIBView: + def __init__(self, doc, dib): + self.dib = dib + self.view = win32ui.CreateView(doc) + self.width = self.height = 0 + # set up message handlers + # self.view.OnPrepareDC = self.OnPrepareDC + self.view.HookMessage(self.OnSize, win32con.WM_SIZE) + + def OnSize(self, params): + lParam = params[3] + self.width = win32api.LOWORD(lParam) + self.height = win32api.HIWORD(lParam) + + def OnDraw(self, ob, dc): + # set sizes used for "non strecth" mode. + self.view.SetScrollSizes(win32con.MM_TEXT, self.dib.GetSize()) + dibSize = self.dib.GetSize() + dibRect = (0, 0, dibSize[0], dibSize[1]) + # stretch BMP. + # self.dib.Paint(dc, (0,0,self.width, self.height),dibRect) + # non stretch. + self.dib.Paint(dc) + + +class DIBDemo: + def __init__(self, filename, *bPBM): + # init data members + f = open(filename, "rb") + dib = win32ui.CreateDIBitmap() + if len(bPBM) > 0: + magic = f.readline() + if magic != "P6\n": + print("The file is not a PBM format file") + raise ValueError("Failed - The file is not a PBM format file") + # check magic? + rowcollist = f.readline().split() + cols = int(rowcollist[0]) + rows = int(rowcollist[1]) + f.readline() # whats this one? + dib.LoadPBMData(f, (cols, rows)) + else: + dib.LoadWindowsFormatFile(f) + f.close() + # create doc/view + self.doc = win32ui.CreateDoc() + self.dibView = DIBView(self.doc, dib) + self.frame = win32ui.CreateMDIFrame() + self.frame.LoadFrame() # this will force OnCreateClient + self.doc.SetTitle("DIB Demo") + self.frame.ShowWindow() + + # display the sucka + self.frame.ActivateFrame() + + def OnCreateClient(self, createparams, context): + self.dibView.view.CreateWindow(self.frame) + return 1 + + +if __name__ == "__main__": + import demoutils + + demoutils.NotAScript() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/Demos/dlgtest.py b/myenv/Lib/site-packages/pythonwin/pywin/Demos/dlgtest.py new file mode 100644 index 000000000..8dcb0e5ef --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/Demos/dlgtest.py @@ -0,0 +1,145 @@ +# A Demo of Pythonwin's Dialog and Property Page support. + +################### +# +# First demo - use the built-in to Pythonwin "Tab Stop" dialog, but +# customise it heavily. +# +# ID's for the tabstop dialog - out test. +# +import win32con +import win32ui +from pywin.mfc import dialog +from win32con import IDCANCEL +from win32ui import IDC_EDIT_TABS, IDC_PROMPT_TABS, IDD_SET_TABSTOPS + + +class TestDialog(dialog.Dialog): + def __init__(self, modal=1): + dialog.Dialog.__init__(self, IDD_SET_TABSTOPS) + self.counter = 0 + if modal: + self.DoModal() + else: + self.CreateWindow() + + def OnInitDialog(self): + # Set the caption of the dialog itself. + self.SetWindowText("Used to be Tab Stops!") + # Get a child control, remember it, and change its text. + self.edit = self.GetDlgItem(IDC_EDIT_TABS) # the text box. + self.edit.SetWindowText("Test") + # Hook a Windows message for the dialog. + self.edit.HookMessage(self.KillFocus, win32con.WM_KILLFOCUS) + # Get the prompt control, and change its next. + prompt = self.GetDlgItem(IDC_PROMPT_TABS) # the prompt box. + prompt.SetWindowText("Prompt") + # And the same for the button.. + cancel = self.GetDlgItem(IDCANCEL) # the cancel button + cancel.SetWindowText("&Kill me") + + # And just for demonstration purposes, we hook the notify message for the dialog. + # This allows us to be notified when the Edit Control text changes. + self.HookCommand(self.OnNotify, IDC_EDIT_TABS) + + def OnNotify(self, controlid, code): + if code == win32con.EN_CHANGE: + print("Edit text changed!") + return 1 # I handled this, so no need to call defaults! + + # kill focus for the edit box. + # Simply increment the value in the text box. + def KillFocus(self, msg): + self.counter = self.counter + 1 + if self.edit != None: + self.edit.SetWindowText(str(self.counter)) + + # Called when the dialog box is terminating... + def OnDestroy(self, msg): + del self.edit + del self.counter + + +# A very simply Property Sheet. +# We only make a new class for demonstration purposes. +class TestSheet(dialog.PropertySheet): + def __init__(self, title): + dialog.PropertySheet.__init__(self, title) + self.HookMessage(self.OnActivate, win32con.WM_ACTIVATE) + + def OnActivate(self, msg): + pass + + +# A very simply Property Page, which will be "owned" by the above +# Property Sheet. +# We create a new class, just so we can hook a control notification. +class TestPage(dialog.PropertyPage): + def OnInitDialog(self): + # We use the HookNotify function to allow Python to respond to + # Windows WM_NOTIFY messages. + # In this case, we are interested in BN_CLICKED messages. + self.HookNotify(self.OnNotify, win32con.BN_CLICKED) + + def OnNotify(self, std, extra): + print("OnNotify", std, extra) + + +# Some code that actually uses these objects. +def demo(modal=0): + TestDialog(modal) + + # property sheet/page demo + ps = win32ui.CreatePropertySheet("Property Sheet/Page Demo") + # Create a completely standard PropertyPage. + page1 = win32ui.CreatePropertyPage(win32ui.IDD_PROPDEMO1) + # Create our custom property page. + page2 = TestPage(win32ui.IDD_PROPDEMO2) + ps.AddPage(page1) + ps.AddPage(page2) + if modal: + ps.DoModal() + else: + style = ( + win32con.WS_SYSMENU + | win32con.WS_POPUP + | win32con.WS_CAPTION + | win32con.DS_MODALFRAME + | win32con.WS_VISIBLE + ) + styleex = win32con.WS_EX_DLGMODALFRAME | win32con.WS_EX_PALETTEWINDOW + ps.CreateWindow(win32ui.GetMainFrame(), style, styleex) + + +def test(modal=1): + # dlg=dialog.Dialog(1010) + # dlg.CreateWindow() + # dlg.EndDialog(0) + # del dlg + # return + # property sheet/page demo + ps = TestSheet("Property Sheet/Page Demo") + page1 = win32ui.CreatePropertyPage(win32ui.IDD_PROPDEMO1) + page2 = win32ui.CreatePropertyPage(win32ui.IDD_PROPDEMO2) + ps.AddPage(page1) + ps.AddPage(page2) + del page1 + del page2 + if modal: + ps.DoModal() + else: + ps.CreateWindow(win32ui.GetMainFrame()) + return ps + + +def d(): + dlg = win32ui.CreateDialog(win32ui.IDD_DEBUGGER) + dlg.datalist.append((win32ui.IDC_DBG_RADIOSTACK, "radio")) + print("data list is ", dlg.datalist) + dlg.data["radio"] = 1 + dlg.DoModal() + print(dlg.data["radio"]) + + +if __name__ == "__main__": + demo(1) diff --git a/myenv/Lib/site-packages/pythonwin/pywin/Demos/dyndlg.py b/myenv/Lib/site-packages/pythonwin/pywin/Demos/dyndlg.py new file mode 100644 index 000000000..46a4e7fc6 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/Demos/dyndlg.py @@ -0,0 +1,104 @@ +# dyndlg.py +# contributed by Curt Hagenlocher + +# Dialog Template params: +# Parameter 0 - Window caption +# Parameter 1 - Bounds (rect tuple) +# Parameter 2 - Window style +# Parameter 3 - Extended style +# Parameter 4 - Font tuple +# Parameter 5 - Menu name +# Parameter 6 - Window class +# Dialog item params: +# Parameter 0 - Window class +# Parameter 1 - Text +# Parameter 2 - ID +# Parameter 3 - Bounds +# Parameter 4 - Style +# Parameter 5 - Extended style +# Parameter 6 - Extra data + + +import win32con +import win32ui +from pywin.mfc import dialog, window + + +def MakeDlgTemplate(): + style = ( + win32con.DS_MODALFRAME + | win32con.WS_POPUP + | win32con.WS_VISIBLE + | win32con.WS_CAPTION + | win32con.WS_SYSMENU + | win32con.DS_SETFONT + ) + cs = win32con.WS_CHILD | win32con.WS_VISIBLE + dlg = [ + ["Select Warehouse", (0, 0, 177, 93), style, None, (8, "MS Sans Serif")], + ] + dlg.append([130, "Current Warehouse:", -1, (7, 7, 69, 9), cs | win32con.SS_LEFT]) + dlg.append([130, "ASTORIA", 128, (16, 17, 99, 7), cs | win32con.SS_LEFT]) + dlg.append([130, "New &Warehouse:", -1, (7, 29, 69, 9), cs | win32con.SS_LEFT]) + s = win32con.WS_TABSTOP | cs + # dlg.append([131, None, 130, (5, 40, 110, 48), + # s | win32con.LBS_NOTIFY | win32con.LBS_SORT | win32con.LBS_NOINTEGRALHEIGHT | win32con.WS_VSCROLL | win32con.WS_BORDER]) + dlg.append( + [ + "{8E27C92B-1264-101C-8A2F-040224009C02}", + None, + 131, + (5, 40, 110, 48), + win32con.WS_TABSTOP, + ] + ) + + dlg.append( + [128, "OK", win32con.IDOK, (124, 5, 50, 14), s | win32con.BS_DEFPUSHBUTTON] + ) + s = win32con.BS_PUSHBUTTON | s + dlg.append([128, "Cancel", win32con.IDCANCEL, (124, 22, 50, 14), s]) + dlg.append([128, "&Help", 100, (124, 74, 50, 14), s]) + + return dlg + + +def test1(): + win32ui.CreateDialogIndirect(MakeDlgTemplate()).DoModal() + + +def test2(): + dialog.Dialog(MakeDlgTemplate()).DoModal() + + +def test3(): + dlg = win32ui.LoadDialogResource(win32ui.IDD_SET_TABSTOPS) + dlg[0][0] = "New Dialog Title" + dlg[0][1] = (80, 20, 161, 60) + dlg[1][1] = "&Confusion:" + cs = ( + win32con.WS_CHILD + | win32con.WS_VISIBLE + | win32con.WS_TABSTOP + | win32con.BS_PUSHBUTTON + ) + dlg.append([128, "&Help", 100, (111, 41, 40, 14), cs]) + dialog.Dialog(dlg).DoModal() + + +def test4(): + page1 = dialog.PropertyPage(win32ui.LoadDialogResource(win32ui.IDD_PROPDEMO1)) + page2 = dialog.PropertyPage(win32ui.LoadDialogResource(win32ui.IDD_PROPDEMO2)) + ps = dialog.PropertySheet("Property Sheet/Page Demo", None, [page1, page2]) + ps.DoModal() + + +def testall(): + test1() + test2() + test3() + test4() + + +if __name__ == "__main__": + testall() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/Demos/fontdemo.py b/myenv/Lib/site-packages/pythonwin/pywin/Demos/fontdemo.py new file mode 100644 index 000000000..fc35f4ca3 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/Demos/fontdemo.py @@ -0,0 +1,86 @@ +# Demo of Generic document windows, DC, and Font usage +# by Dave Brennan (brennan@hal.com) + +# usage examples: + +# >>> from fontdemo import * +# >>> d = FontDemo('Hello, Python') +# >>> f1 = { 'name':'Arial', 'height':36, 'weight':win32con.FW_BOLD} +# >>> d.SetFont(f1) +# >>> f2 = {'name':'Courier New', 'height':24, 'italic':1} +# >>> d.SetFont (f2) + +import win32api +import win32con +import win32ui +from pywin.mfc import docview + +# font is a dictionary in which the following elements matter: +# (the best matching font to supplied parameters is returned) +# name string name of the font as known by Windows +# size point size of font in logical units +# weight weight of font (win32con.FW_NORMAL, win32con.FW_BOLD) +# italic boolean; true if set to anything but None +# underline boolean; true if set to anything but None + + +class FontView(docview.ScrollView): + def __init__( + self, doc, text="Python Rules!", font_spec={"name": "Arial", "height": 42} + ): + docview.ScrollView.__init__(self, doc) + self.font = win32ui.CreateFont(font_spec) + self.text = text + self.width = self.height = 0 + # set up message handlers + self.HookMessage(self.OnSize, win32con.WM_SIZE) + + def OnAttachedObjectDeath(self): + docview.ScrollView.OnAttachedObjectDeath(self) + del self.font + + def SetFont(self, new_font): + # Change font on the fly + self.font = win32ui.CreateFont(new_font) + # redraw the entire client window + selfInvalidateRect(None) + + def OnSize(self, params): + lParam = params[3] + self.width = win32api.LOWORD(lParam) + self.height = win32api.HIWORD(lParam) + + def OnPrepareDC(self, dc, printinfo): + # Set up the DC for forthcoming OnDraw call + self.SetScrollSizes(win32con.MM_TEXT, (100, 100)) + dc.SetTextColor(win32api.RGB(0, 0, 255)) + dc.SetBkColor(win32api.GetSysColor(win32con.COLOR_WINDOW)) + dc.SelectObject(self.font) + dc.SetTextAlign(win32con.TA_CENTER | win32con.TA_BASELINE) + + def OnDraw(self, dc): + if self.width == 0 and self.height == 0: + left, top, right, bottom = self.GetClientRect() + self.width = right - left + self.height = bottom - top + x, y = self.width // 2, self.height // 2 + dc.TextOut(x, y, self.text) + + +def FontDemo(): + # create doc/view + template = docview.DocTemplate(win32ui.IDR_PYTHONTYPE, None, None, FontView) + doc = template.OpenDocumentFile(None) + doc.SetTitle("Font Demo") + # print "template is ", template, "obj is", template._obj_ + template.close() + + +# print "closed" +# del template + +if __name__ == "__main__": + import demoutils + + if demoutils.NeedGoodGUI(): + FontDemo() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/Demos/guidemo.py b/myenv/Lib/site-packages/pythonwin/pywin/Demos/guidemo.py new file mode 100644 index 000000000..b4d88159a --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/Demos/guidemo.py @@ -0,0 +1,86 @@ +# GUI Demo - just a worker script to invoke all the other demo/test scripts. +import sys + +import __main__ +import regutil +import win32api +import win32ui + +demos = [ # ('Font', 'import fontdemo;fontdemo.FontDemo()'), + ("Open GL Demo", "import openGLDemo;openGLDemo.test()"), + ("Threaded GUI", "import threadedgui;threadedgui.ThreadedDemo()"), + ("Tree View Demo", "import hiertest;hiertest.demoboth()"), + ("3-Way Splitter Window", "import splittst;splittst.demo()"), + ("Custom Toolbars and Tooltips", "import toolbar;toolbar.test()"), + ("Progress Bar", "import progressbar;progressbar.demo()"), + ("Slider Control", "import sliderdemo;sliderdemo.demo()"), + ("Dynamic window creation", "import createwin;createwin.demo()"), + ("Various Dialog demos", "import dlgtest;dlgtest.demo()"), + ("OCX Control Demo", "from ocx import ocxtest;ocxtest.demo()"), + ("OCX Serial Port Demo", "from ocx import ocxserialtest; ocxserialtest.test()"), + ( + "IE4 Control Demo", + 'from ocx import webbrowser; webbrowser.Demo("http://www.python.org")', + ), +] + + +def demo(): + try: + # seeif I can locate the demo files. + import fontdemo + except ImportError: + # else put the demos direectory on the path (if not already) + try: + instPath = regutil.GetRegistryDefaultValue( + regutil.BuildDefaultPythonKey() + "\\InstallPath" + ) + except win32api.error: + print( + "The InstallPath can not be located, and the Demos directory is not on the path" + ) + instPath = "." + + demosDir = win32ui.FullPath(instPath + "\\Demos") + for path in sys.path: + if win32ui.FullPath(path) == demosDir: + break + else: + sys.path.append(demosDir) + import fontdemo + + import sys + + if "/go" in sys.argv: + for name, cmd in demos: + try: + exec(cmd) + except: + print( + "Demo of %s failed - %s:%s" + % (cmd, sys.exc_info()[0], sys.exc_info()[1]) + ) + return + # Otherwise allow the user to select the demo to run + + import pywin.dialogs.list + + while 1: + rc = pywin.dialogs.list.SelectFromLists("Select a Demo", demos, ["Demo Title"]) + if rc is None: + break + title, cmd = demos[rc] + try: + exec(cmd) + except: + print( + "Demo of %s failed - %s:%s" + % (title, sys.exc_info()[0], sys.exc_info()[1]) + ) + + +if __name__ == __main__.__name__: + import demoutils + + if demoutils.NeedGoodGUI(): + demo() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/Demos/hiertest.py b/myenv/Lib/site-packages/pythonwin/pywin/Demos/hiertest.py new file mode 100644 index 000000000..287b71daf --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/Demos/hiertest.py @@ -0,0 +1,138 @@ +import os + +import commctrl +import win32ui +from pywin.mfc import docview, window +from pywin.tools import hierlist + + +# directory listbox +# This has obvious limitations - doesnt track subdirs, etc. Demonstrates +# simple use of Python code for querying the tree as needed. +# Only use strings, and lists of strings (from curdir()) +class DirHierList(hierlist.HierList): + def __init__(self, root, listBoxID=win32ui.IDC_LIST1): + hierlist.HierList.__init__(self, root, win32ui.IDB_HIERFOLDERS, listBoxID) + + def GetText(self, item): + return os.path.basename(item) + + def GetSubList(self, item): + if os.path.isdir(item): + ret = [os.path.join(item, fname) for fname in os.listdir(item)] + else: + ret = None + return ret + + # if the item is a dir, it is expandable. + def IsExpandable(self, item): + return os.path.isdir(item) + + def GetSelectedBitmapColumn(self, item): + return self.GetBitmapColumn(item) + 6 # Use different color for selection + + +class TestDocument(docview.Document): + def __init__(self, template): + docview.Document.__init__(self, template) + self.hierlist = hierlist.HierListWithItems( + HLIFileDir("\\"), win32ui.IDB_HIERFOLDERS, win32ui.AFX_IDW_PANE_FIRST + ) + + +class HierListView(docview.TreeView): + def OnInitialUpdate(self): + rc = self._obj_.OnInitialUpdate() + self.hierList = self.GetDocument().hierlist + self.hierList.HierInit(self.GetParent()) + self.hierList.SetStyle( + commctrl.TVS_HASLINES | commctrl.TVS_LINESATROOT | commctrl.TVS_HASBUTTONS + ) + return rc + + +class HierListFrame(window.MDIChildWnd): + pass + + +def GetTestRoot(): + tree1 = ("Tree 1", [("Item 1", "Item 1 data"), "Item 2", 3]) + tree2 = ("Tree 2", [("Item 2.1", "Item 2 data"), "Item 2.2", 2.3]) + return ("Root", [tree1, tree2, "Item 3"]) + + +def demoboth(): + template = docview.DocTemplate( + win32ui.IDR_PYTHONTYPE, TestDocument, HierListFrame, HierListView + ) + template.OpenDocumentFile(None).SetTitle("Hierlist demo") + + demomodeless() + + +def demomodeless(): + testList2 = DirHierList("\\") + dlg = hierlist.HierDialog("hier list test", testList2) + dlg.CreateWindow() + + +def demodlg(): + testList2 = DirHierList("\\") + dlg = hierlist.HierDialog("hier list test", testList2) + dlg.DoModal() + + +def demo(): + template = docview.DocTemplate( + win32ui.IDR_PYTHONTYPE, TestDocument, HierListFrame, HierListView + ) + template.OpenDocumentFile(None).SetTitle("Hierlist demo") + + +# +# Demo/Test for HierList items. +# +# Easy to make a better directory program. +# +class HLIFileDir(hierlist.HierListItem): + def __init__(self, filename): + self.filename = filename + hierlist.HierListItem.__init__(self) + + def GetText(self): + try: + return "%-20s %d bytes" % ( + os.path.basename(self.filename), + os.stat(self.filename)[6], + ) + except os.error as details: + return "%-20s - %s" % (self.filename, details[1]) + + def IsExpandable(self): + return os.path.isdir(self.filename) + + def GetSubList(self): + ret = [] + for newname in os.listdir(self.filename): + if newname not in (".", ".."): + ret.append(HLIFileDir(os.path.join(self.filename, newname))) + return ret + + +def demohli(): + template = docview.DocTemplate( + win32ui.IDR_PYTHONTYPE, + TestDocument, + hierlist.HierListFrame, + hierlist.HierListView, + ) + template.OpenDocumentFile(None).SetTitle("Hierlist demo") + + +if __name__ == "__main__": + import demoutils + + if demoutils.HaveGoodGUI(): + demoboth() + else: + demodlg() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/Demos/menutest.py b/myenv/Lib/site-packages/pythonwin/pywin/Demos/menutest.py new file mode 100644 index 000000000..d2ba65b6d --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/Demos/menutest.py @@ -0,0 +1,13 @@ +# Run this as a python script, to gray "close" off the edit window system menu. +import win32con +from pywin.framework import interact + +if __name__ == "__main__": + import demoutils + + if demoutils.NeedGoodGUI(): + win = interact.edit.currentView.GetParent() + menu = win.GetSystemMenu() + id = menu.GetMenuItemID(6) + menu.EnableMenuItem(id, win32con.MF_BYCOMMAND | win32con.MF_GRAYED) + print("The interactive window's 'Close' menu item is now disabled.") diff --git a/myenv/Lib/site-packages/pythonwin/pywin/Demos/objdoc.py b/myenv/Lib/site-packages/pythonwin/pywin/Demos/objdoc.py new file mode 100644 index 000000000..fff792536 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/Demos/objdoc.py @@ -0,0 +1,57 @@ +# This is a sample file, and shows the basic framework for using an "Object" based +# document, rather than a "filename" based document. +# This is referenced by the Pythonwin .html documentation. + +# In the example below, the OpenObject() method is used instead of OpenDocumentFile, +# and all the core MFC document open functionality is retained. + +import win32ui +from pywin.mfc import docview + + +class object_template(docview.DocTemplate): + def __init__(self): + docview.DocTemplate.__init__(self, None, None, None, object_view) + + def OpenObject(self, object): # Use this instead of OpenDocumentFile. + # Look for existing open document + for doc in self.GetDocumentList(): + print("document is ", doc) + if doc.object is object: + doc.GetFirstView().ActivateFrame() + return doc + # not found - new one. + doc = object_document(self, object) + frame = self.CreateNewFrame(doc) + doc.OnNewDocument() + doc.SetTitle(str(object)) + self.InitialUpdateFrame(frame, doc) + return doc + + +class object_document(docview.Document): + def __init__(self, template, object): + docview.Document.__init__(self, template) + self.object = object + + def OnOpenDocument(self, name): + raise RuntimeError("Should not be called if template strings set up correctly") + return 0 + + +class object_view(docview.EditView): + def OnInitialUpdate(self): + self.ReplaceSel("Object is %s" % repr(self.GetDocument().object)) + + +def demo(): + t = object_template() + d = t.OpenObject(win32ui) + return (t, d) + + +if __name__ == "__main__": + import demoutils + + if demoutils.NeedGoodGUI(): + demo() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/Demos/ocx/__init__.py b/myenv/Lib/site-packages/pythonwin/pywin/Demos/ocx/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/myenv/Lib/site-packages/pythonwin/pywin/Demos/ocx/demoutils.py b/myenv/Lib/site-packages/pythonwin/pywin/Demos/ocx/demoutils.py new file mode 100644 index 000000000..27fa091cb --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/Demos/ocx/demoutils.py @@ -0,0 +1,67 @@ +# Utilities for the demos + +import sys + +import win32api +import win32con +import win32ui + +NotScriptMsg = """\ +This demo program is not designed to be run as a Script, but is +probably used by some other test program. Please try another demo. +""" + +NeedGUIMsg = """\ +This demo program can only be run from inside of Pythonwin + +You must start Pythonwin, and select 'Run' from the toolbar or File menu +""" + + +NeedAppMsg = """\ +This demo program is a 'Pythonwin Application'. + +It is more demo code than an example of Pythonwin's capabilities. + +To run it, you must execute the command: +pythonwin.exe /app "%s" + +Would you like to execute it now? +""" + + +def NotAScript(): + import win32ui + + win32ui.MessageBox(NotScriptMsg, "Demos") + + +def NeedGoodGUI(): + from pywin.framework.app import HaveGoodGUI + + rc = HaveGoodGUI() + if not rc: + win32ui.MessageBox(NeedGUIMsg, "Demos") + return rc + + +def NeedApp(): + import win32ui + + rc = win32ui.MessageBox(NeedAppMsg % sys.argv[0], "Demos", win32con.MB_YESNO) + if rc == win32con.IDYES: + try: + parent = win32ui.GetMainFrame().GetSafeHwnd() + win32api.ShellExecute( + parent, None, "pythonwin.exe", '/app "%s"' % sys.argv[0], None, 1 + ) + except win32api.error as details: + win32ui.MessageBox("Error executing command - %s" % (details), "Demos") + + +from pywin.framework.app import HaveGoodGUI + +if __name__ == "__main__": + from . import demoutils + + demoutils.NotAScript() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/Demos/ocx/flash.py b/myenv/Lib/site-packages/pythonwin/pywin/Demos/ocx/flash.py new file mode 100644 index 000000000..239240ee3 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/Demos/ocx/flash.py @@ -0,0 +1,95 @@ +# By Bradley Schatz +# simple flash/python application demonstrating bidirectional +# communicaion between flash and python. Click the sphere to see +# behavior. Uses Bounce.swf from FlashBounce.zip, available from +# http://pages.cpsc.ucalgary.ca/~saul/vb_examples/tutorial12/ + +# Update to the path of the .swf file (note it could be a true URL) +flash_url = "c:\\bounce.swf" + +import sys + +import regutil +import win32api +import win32con +import win32ui +from pywin.mfc import activex, window +from win32com.client import gencache + +FlashModule = gencache.EnsureModule("{D27CDB6B-AE6D-11CF-96B8-444553540000}", 0, 1, 0) + +if FlashModule is None: + raise ImportError("Flash does not appear to be installed.") + + +class MyFlashComponent(activex.Control, FlashModule.ShockwaveFlash): + def __init__(self): + activex.Control.__init__(self) + FlashModule.ShockwaveFlash.__init__(self) + self.x = 50 + self.y = 50 + self.angle = 30 + self.started = 0 + + def OnFSCommand(self, command, args): + print("FSCommend", command, args) + self.x = self.x + 20 + self.y = self.y + 20 + self.angle = self.angle + 20 + if self.x > 200 or self.y > 200: + self.x = 0 + self.y = 0 + if self.angle > 360: + self.angle = 0 + self.SetVariable("xVal", self.x) + self.SetVariable("yVal", self.y) + self.SetVariable("angle", self.angle) + self.TPlay("_root.mikeBall") + + def OnProgress(self, percentDone): + print("PercentDone", percentDone) + + def OnReadyStateChange(self, newState): + # 0=Loading, 1=Uninitialized, 2=Loaded, 3=Interactive, 4=Complete + print("State", newState) + + +class BrowserFrame(window.MDIChildWnd): + def __init__(self, url=None): + if url is None: + self.url = regutil.GetRegisteredHelpFile("Main Python Documentation") + else: + self.url = url + pass # Dont call base class doc/view version... + + def Create(self, title, rect=None, parent=None): + style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_OVERLAPPEDWINDOW + self._obj_ = win32ui.CreateMDIChild() + self._obj_.AttachObject(self) + self._obj_.CreateWindow(None, title, style, rect, parent) + rect = self.GetClientRect() + rect = (0, 0, rect[2] - rect[0], rect[3] - rect[1]) + self.ocx = MyFlashComponent() + self.ocx.CreateControl( + "Flash Player", win32con.WS_VISIBLE | win32con.WS_CHILD, rect, self, 1000 + ) + self.ocx.LoadMovie(0, flash_url) + self.ocx.Play() + self.HookMessage(self.OnSize, win32con.WM_SIZE) + + def OnSize(self, params): + rect = self.GetClientRect() + rect = (0, 0, rect[2] - rect[0], rect[3] - rect[1]) + self.ocx.SetWindowPos(0, rect, 0) + + +def Demo(): + url = None + if len(sys.argv) > 1: + url = win32api.GetFullPathName(sys.argv[1]) + f = BrowserFrame(url) + f.Create("Flash Player") + + +if __name__ == "__main__": + Demo() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/Demos/ocx/msoffice.py b/myenv/Lib/site-packages/pythonwin/pywin/Demos/ocx/msoffice.py new file mode 100644 index 000000000..be24ead82 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/Demos/ocx/msoffice.py @@ -0,0 +1,152 @@ +# This demo uses some of the Microsoft Office components. +# +# It was taken from an MSDN article showing how to embed excel. +# It is not comlpete yet, but it _does_ show an Excel spreadsheet in a frame! +# + +import regutil +import win32con +import win32ui +import win32uiole +from pywin.mfc import activex, docview, object, window +from win32com.client import gencache + +# WordModule = gencache.EnsureModule('{00020905-0000-0000-C000-000000000046}', 1033, 8, 0) +# if WordModule is None: +# raise ImportError, "Microsoft Word version 8 does not appear to be installed." + + +class OleClientItem(object.CmdTarget): + def __init__(self, doc): + object.CmdTarget.__init__(self, win32uiole.CreateOleClientItem(doc)) + + def OnGetItemPosition(self): + # For now return a hard-coded rect. + return (10, 10, 210, 210) + + def OnActivate(self): + # Allow only one inplace activate item per frame + view = self.GetActiveView() + item = self.GetDocument().GetInPlaceActiveItem(view) + if item is not None and item._obj_ != self._obj_: + item.Close() + self._obj_.OnActivate() + + def OnChange(self, oleNotification, dwParam): + self._obj_.OnChange(oleNotification, dwParam) + self.GetDocument().UpdateAllViews(None) + + def OnChangeItemPosition(self, rect): + # During in-place activation CEmbed_ExcelCntrItem::OnChangeItemPosition + # is called by the server to change the position of the in-place + # window. Usually, this is a result of the data in the server + # document changing such that the extent has changed or as a result + # of in-place resizing. + # + # The default here is to call the base class, which will call + # COleClientItem::SetItemRects to move the item + # to the new position. + if not self._obj_.OnChangeItemPosition(self, rect): + return 0 + + # TODO: update any cache you may have of the item's rectangle/extent + return 1 + + +class OleDocument(object.CmdTarget): + def __init__(self, template): + object.CmdTarget.__init__(self, win32uiole.CreateOleDocument(template)) + self.EnableCompoundFile() + + +class ExcelView(docview.ScrollView): + def OnInitialUpdate(self): + self.HookMessage(self.OnSetFocus, win32con.WM_SETFOCUS) + self.HookMessage(self.OnSize, win32con.WM_SIZE) + + self.SetScrollSizes(win32con.MM_TEXT, (100, 100)) + rc = self._obj_.OnInitialUpdate() + self.EmbedExcel() + return rc + + def EmbedExcel(self): + doc = self.GetDocument() + self.clientItem = OleClientItem(doc) + self.clientItem.CreateNewItem("Excel.Sheet") + self.clientItem.DoVerb(-1, self) + doc.UpdateAllViews(None) + + def OnDraw(self, dc): + doc = self.GetDocument() + pos = doc.GetStartPosition() + clientItem, pos = doc.GetNextItem(pos) + clientItem.Draw(dc, (10, 10, 210, 210)) + + # Special handling of OnSetFocus and OnSize are required for a container + # when an object is being edited in-place. + def OnSetFocus(self, msg): + item = self.GetDocument().GetInPlaceActiveItem(self) + if ( + item is not None + and item.GetItemState() == win32uiole.COleClientItem_activeUIState + ): + wnd = item.GetInPlaceWindow() + if wnd is not None: + wnd.SetFocus() + return 0 # Dont get the base version called. + return 1 # Call the base version. + + def OnSize(self, params): + item = self.GetDocument().GetInPlaceActiveItem(self) + if item is not None: + item.SetItemRects() + return 1 # do call the base! + + +class OleTemplate(docview.DocTemplate): + def __init__( + self, resourceId=None, MakeDocument=None, MakeFrame=None, MakeView=None + ): + if MakeDocument is None: + MakeDocument = OleDocument + if MakeView is None: + MakeView = ExcelView + docview.DocTemplate.__init__( + self, resourceId, MakeDocument, MakeFrame, MakeView + ) + + +class WordFrame(window.MDIChildWnd): + def __init__(self, doc=None): + self._obj_ = win32ui.CreateMDIChild() + self._obj_.AttachObject(self) + # Dont call base class doc/view version... + + def Create(self, title, rect=None, parent=None): + style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_OVERLAPPEDWINDOW + self._obj_.CreateWindow(None, title, style, rect, parent) + + rect = self.GetClientRect() + rect = (0, 0, rect[2] - rect[0], rect[3] - rect[1]) + self.ocx = MyWordControl() + self.ocx.CreateControl( + "Microsoft Word", win32con.WS_VISIBLE | win32con.WS_CHILD, rect, self, 20000 + ) + + +def Demo(): + import sys + + import win32api + + docName = None + if len(sys.argv) > 1: + docName = win32api.GetFullPathName(sys.argv[1]) + OleTemplate().OpenDocumentFile(None) + + +# f = WordFrame(docName) +# f.Create("Microsoft Office") + +if __name__ == "__main__": + Demo() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/Demos/ocx/ocxserialtest.py b/myenv/Lib/site-packages/pythonwin/pywin/Demos/ocx/ocxserialtest.py new file mode 100644 index 000000000..326d312c8 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/Demos/ocx/ocxserialtest.py @@ -0,0 +1,133 @@ +# ocxserialtest.py +# +# Sample that uses the mscomm OCX to talk to a serial +# device. + +# Very simple - queries a modem for ATI responses + +import pythoncom +import win32con +import win32ui +import win32uiole +from pywin.mfc import activex, dialog +from win32com.client import gencache + +SERIAL_SETTINGS = "19200,n,8,1" +SERIAL_PORT = 2 + +win32ui.DoWaitCursor(1) +serialModule = gencache.EnsureModule("{648A5603-2C6E-101B-82B6-000000000014}", 0, 1, 1) +win32ui.DoWaitCursor(0) +if serialModule is None: + raise ImportError("MS COMM Control does not appear to be installed on the PC") + + +def MakeDlgTemplate(): + style = ( + win32con.DS_MODALFRAME + | win32con.WS_POPUP + | win32con.WS_VISIBLE + | win32con.WS_CAPTION + | win32con.WS_SYSMENU + | win32con.DS_SETFONT + ) + cs = win32con.WS_CHILD | win32con.WS_VISIBLE + dlg = [ + ["Very Basic Terminal", (0, 0, 350, 180), style, None, (8, "MS Sans Serif")], + ] + s = win32con.WS_TABSTOP | cs + dlg.append( + [ + "RICHEDIT", + None, + 132, + (5, 5, 340, 170), + s + | win32con.ES_WANTRETURN + | win32con.ES_MULTILINE + | win32con.ES_AUTOVSCROLL + | win32con.WS_VSCROLL, + ] + ) + return dlg + + +#################################### +# +# Serial Control +# +class MySerialControl(activex.Control, serialModule.MSComm): + def __init__(self, parent): + activex.Control.__init__(self) + serialModule.MSComm.__init__(self) + self.parent = parent + + def OnComm(self): + self.parent.OnComm() + + +class TestSerDialog(dialog.Dialog): + def __init__(self, *args): + dialog.Dialog.__init__(*(self,) + args) + self.olectl = None + + def OnComm(self): + event = self.olectl.CommEvent + if event == serialModule.OnCommConstants.comEvReceive: + self.editwindow.ReplaceSel(self.olectl.Input) + + def OnKey(self, key): + if self.olectl: + self.olectl.Output = chr(key) + + def OnInitDialog(self): + rc = dialog.Dialog.OnInitDialog(self) + self.editwindow = self.GetDlgItem(132) + self.editwindow.HookAllKeyStrokes(self.OnKey) + + self.olectl = MySerialControl(self) + try: + self.olectl.CreateControl( + "OCX", + win32con.WS_TABSTOP | win32con.WS_VISIBLE, + (7, 43, 500, 300), + self._obj_, + 131, + ) + except win32ui.error: + self.MessageBox("The Serial Control could not be created") + self.olectl = None + self.EndDialog(win32con.IDCANCEL) + if self.olectl: + self.olectl.Settings = SERIAL_SETTINGS + self.olectl.CommPort = SERIAL_PORT + self.olectl.RThreshold = 1 + try: + self.olectl.PortOpen = 1 + except pythoncom.com_error as details: + print( + "Could not open the specified serial port - %s" + % (details.excepinfo[2]) + ) + self.EndDialog(win32con.IDCANCEL) + return rc + + def OnDestroy(self, msg): + if self.olectl: + try: + self.olectl.PortOpen = 0 + except pythoncom.com_error as details: + print("Error closing port - %s" % (details.excepinfo[2])) + return dialog.Dialog.OnDestroy(self, msg) + + +def test(): + d = TestSerDialog(MakeDlgTemplate()) + d.DoModal() + + +if __name__ == "__main__": + from . import demoutils + + if demoutils.NeedGoodGUI(): + test() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/Demos/ocx/ocxtest.py b/myenv/Lib/site-packages/pythonwin/pywin/Demos/ocx/ocxtest.py new file mode 100644 index 000000000..4a3d73363 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/Demos/ocx/ocxtest.py @@ -0,0 +1,250 @@ +# OCX Tester for Pythonwin +# +# This file _is_ ready to run. All that is required is that the OCXs being tested +# are installed on your machine. +# +# The .py files behind the OCXs will be automatically generated and imported. + +import glob +import os + +import win32api +import win32con +import win32ui +import win32uiole +from pywin.mfc import activex, dialog, window +from win32com.client import gencache + + +def MakeDlgTemplate(): + style = ( + win32con.DS_MODALFRAME + | win32con.WS_POPUP + | win32con.WS_VISIBLE + | win32con.WS_CAPTION + | win32con.WS_SYSMENU + | win32con.DS_SETFONT + ) + cs = win32con.WS_CHILD | win32con.WS_VISIBLE + dlg = [ + ["OCX Demos", (0, 0, 350, 350), style, None, (8, "MS Sans Serif")], + ] + s = win32con.WS_TABSTOP | cs + # dlg.append([131, None, 130, (5, 40, 110, 48), + # s | win32con.LBS_NOTIFY | win32con.LBS_SORT | win32con.LBS_NOINTEGRALHEIGHT | win32con.WS_VSCROLL | win32con.WS_BORDER]) + # dlg.append(["{8E27C92B-1264-101C-8A2F-040224009C02}", None, 131, (5, 40, 110, 48),win32con.WS_TABSTOP]) + + dlg.append( + [128, "About", win32con.IDOK, (124, 5, 50, 14), s | win32con.BS_DEFPUSHBUTTON] + ) + s = win32con.BS_PUSHBUTTON | s + dlg.append([128, "Close", win32con.IDCANCEL, (124, 22, 50, 14), s]) + + return dlg + + +#################################### +# +# Calendar test code +# + + +def GetTestCalendarClass(): + global calendarParentModule + win32ui.DoWaitCursor(1) + calendarParentModule = gencache.EnsureModule( + "{8E27C92E-1264-101C-8A2F-040224009C02}", 0, 7, 0 + ) + win32ui.DoWaitCursor(0) + if calendarParentModule is None: + return None + + class TestCalDialog(dialog.Dialog): + def OnInitDialog(self): + class MyCal(activex.Control, calendarParentModule.Calendar): + def OnAfterUpdate(self): + print("OnAfterUpdate") + + def OnClick(self): + print("OnClick") + + def OnDblClick(self): + print("OnDblClick") + + def OnKeyDown(self, KeyCode, Shift): + print("OnKeyDown", KeyCode, Shift) + + def OnKeyPress(self, KeyAscii): + print("OnKeyPress", KeyAscii) + + def OnKeyUp(self, KeyCode, Shift): + print("OnKeyUp", KeyCode, Shift) + + def OnBeforeUpdate(self, Cancel): + print("OnBeforeUpdate", Cancel) + + def OnNewMonth(self): + print("OnNewMonth") + + def OnNewYear(self): + print("OnNewYear") + + rc = dialog.Dialog.OnInitDialog(self) + self.olectl = MyCal() + try: + self.olectl.CreateControl( + "OCX", + win32con.WS_TABSTOP | win32con.WS_VISIBLE, + (7, 43, 500, 300), + self._obj_, + 131, + ) + except win32ui.error: + self.MessageBox("The Calendar Control could not be created") + self.olectl = None + self.EndDialog(win32con.IDCANCEL) + + return rc + + def OnOK(self): + self.olectl.AboutBox() + + return TestCalDialog + + +#################################### +# +# Video Control +# +def GetTestVideoModule(): + global videoControlModule, videoControlFileName + win32ui.DoWaitCursor(1) + videoControlModule = gencache.EnsureModule( + "{05589FA0-C356-11CE-BF01-00AA0055595A}", 0, 2, 0 + ) + win32ui.DoWaitCursor(0) + if videoControlModule is None: + return None + fnames = glob.glob(os.path.join(win32api.GetWindowsDirectory(), "*.avi")) + if not fnames: + print("No AVI files available in system directory") + return None + videoControlFileName = fnames[0] + return videoControlModule + + +def GetTestVideoDialogClass(): + if GetTestVideoModule() is None: + return None + + class TestVideoDialog(dialog.Dialog): + def OnInitDialog(self): + rc = dialog.Dialog.OnInitDialog(self) + try: + self.olectl = activex.MakeControlInstance( + videoControlModule.ActiveMovie + ) + self.olectl.CreateControl( + "", + win32con.WS_TABSTOP | win32con.WS_VISIBLE, + (7, 43, 500, 300), + self._obj_, + 131, + ) + except win32ui.error: + self.MessageBox("The Video Control could not be created") + self.olectl = None + self.EndDialog(win32con.IDCANCEL) + return + + self.olectl.FileName = videoControlFileName + # self.olectl.Run() + return rc + + def OnOK(self): + self.olectl.AboutBox() + + return TestVideoDialog + + +############### +# +# An OCX in an MDI Frame +# +class OCXFrame(window.MDIChildWnd): + def __init__(self): + pass # Dont call base class doc/view version... + + def Create(self, controlClass, title, rect=None, parent=None): + style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_OVERLAPPEDWINDOW + self._obj_ = win32ui.CreateMDIChild() + self._obj_.AttachObject(self) + self._obj_.CreateWindow(None, title, style, rect, parent) + + rect = self.GetClientRect() + rect = (0, 0, rect[2] - rect[0], rect[3] - rect[1]) + self.ocx = controlClass() + self.ocx.CreateControl( + "", win32con.WS_VISIBLE | win32con.WS_CHILD, rect, self, 1000 + ) + + +def MDITest(): + calendarParentModule = gencache.EnsureModule( + "{8E27C92E-1264-101C-8A2F-040224009C02}", 0, 7, 0 + ) + + class MyCal(activex.Control, calendarParentModule.Calendar): + def OnAfterUpdate(self): + print("OnAfterUpdate") + + def OnClick(self): + print("OnClick") + + f = OCXFrame() + f.Create(MyCal, "Calendar Test") + + +def test1(): + klass = GetTestCalendarClass() + if klass is None: + print( + "Can not test the MSAccess Calendar control - it does not appear to be installed" + ) + return + + d = klass(MakeDlgTemplate()) + d.DoModal() + + +def test2(): + klass = GetTestVideoDialogClass() + if klass is None: + print("Can not test the Video OCX - it does not appear to be installed,") + print("or no AVI files can be found.") + return + d = klass(MakeDlgTemplate()) + d.DoModal() + d = None + + +def test3(): + d = TestCOMMDialog(MakeDlgTemplate()) + d.DoModal() + d = None + + +def testall(): + test1() + test2() + + +def demo(): + testall() + + +if __name__ == "__main__": + from . import demoutils + + if demoutils.NeedGoodGUI(): + testall() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/Demos/ocx/webbrowser.py b/myenv/Lib/site-packages/pythonwin/pywin/Demos/ocx/webbrowser.py new file mode 100644 index 000000000..cc17445e9 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/Demos/ocx/webbrowser.py @@ -0,0 +1,72 @@ +# This demo uses the IE4 Web Browser control. + +# It catches an "OnNavigate" event, and updates the frame title. +# (event stuff by Neil Hodgson) + +import sys + +import regutil +import win32api +import win32con +import win32ui +from pywin.mfc import activex, window +from win32com.client import gencache + +WebBrowserModule = gencache.EnsureModule( + "{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}", 0, 1, 1 +) +if WebBrowserModule is None: + raise ImportError("IE4 does not appear to be installed.") + + +class MyWebBrowser(activex.Control, WebBrowserModule.WebBrowser): + def OnBeforeNavigate2( + self, pDisp, URL, Flags, TargetFrameName, PostData, Headers, Cancel + ): + self.GetParent().OnNavigate(URL) + # print "BeforeNavigate2", pDisp, URL, Flags, TargetFrameName, PostData, Headers, Cancel + + +class BrowserFrame(window.MDIChildWnd): + def __init__(self, url=None): + if url is None: + self.url = regutil.GetRegisteredHelpFile("Main Python Documentation") + if self.url is None: + self.url = "http://www.python.org" + else: + self.url = url + pass # Dont call base class doc/view version... + + def Create(self, title, rect=None, parent=None): + style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_OVERLAPPEDWINDOW + self._obj_ = win32ui.CreateMDIChild() + self._obj_.AttachObject(self) + self._obj_.CreateWindow(None, title, style, rect, parent) + rect = self.GetClientRect() + rect = (0, 0, rect[2] - rect[0], rect[3] - rect[1]) + self.ocx = MyWebBrowser() + self.ocx.CreateControl( + "Web Browser", win32con.WS_VISIBLE | win32con.WS_CHILD, rect, self, 1000 + ) + self.ocx.Navigate(self.url) + self.HookMessage(self.OnSize, win32con.WM_SIZE) + + def OnSize(self, params): + rect = self.GetClientRect() + rect = (0, 0, rect[2] - rect[0], rect[3] - rect[1]) + self.ocx.SetWindowPos(0, rect, 0) + + def OnNavigate(self, url): + title = "Web Browser - %s" % (url,) + self.SetWindowText(title) + + +def Demo(url=None): + if url is None and len(sys.argv) > 1: + url = win32api.GetFullPathName(sys.argv[1]) + f = BrowserFrame(url) + f.Create("Web Browser") + + +if __name__ == "__main__": + Demo() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/Demos/openGLDemo.py b/myenv/Lib/site-packages/pythonwin/pywin/Demos/openGLDemo.py new file mode 100644 index 000000000..9274949de --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/Demos/openGLDemo.py @@ -0,0 +1,370 @@ +# Ported from the win32 and MFC OpenGL Samples. + +import sys + +from pywin.mfc import docview + +try: + from OpenGL.GL import * # nopycln: import + from OpenGL.GLU import * # nopycln: import +except ImportError: + print("The OpenGL extensions do not appear to be installed.") + print("This Pythonwin demo can not run") + sys.exit(1) + +import timer +import win32api +import win32con +import win32ui + +PFD_TYPE_RGBA = 0 +PFD_TYPE_COLORINDEX = 1 +PFD_MAIN_PLANE = 0 +PFD_OVERLAY_PLANE = 1 +PFD_UNDERLAY_PLANE = -1 +PFD_DOUBLEBUFFER = 0x00000001 +PFD_STEREO = 0x00000002 +PFD_DRAW_TO_WINDOW = 0x00000004 +PFD_DRAW_TO_BITMAP = 0x00000008 +PFD_SUPPORT_GDI = 0x00000010 +PFD_SUPPORT_OPENGL = 0x00000020 +PFD_GENERIC_FORMAT = 0x00000040 +PFD_NEED_PALETTE = 0x00000080 +PFD_NEED_SYSTEM_PALETTE = 0x00000100 +PFD_SWAP_EXCHANGE = 0x00000200 +PFD_SWAP_COPY = 0x00000400 +PFD_SWAP_LAYER_BUFFERS = 0x00000800 +PFD_GENERIC_ACCELERATED = 0x00001000 +PFD_DEPTH_DONTCARE = 0x20000000 +PFD_DOUBLEBUFFER_DONTCARE = 0x40000000 +PFD_STEREO_DONTCARE = 0x80000000 + + +# threeto8 = [0, 0o111>>1, 0o222>>1, 0o333>>1, 0o444>>1, 0o555>>1, 0o666>>1, 0o377] +threeto8 = [0, 73 >> 1, 146 >> 1, 219 >> 1, 292 >> 1, 365 >> 1, 438 >> 1, 255] +twoto8 = [0, 0x55, 0xAA, 0xFF] +oneto8 = [0, 255] + + +def ComponentFromIndex(i, nbits, shift): + # val = (unsigned char) (i >> shift); + val = (i >> shift) & 0xF + if nbits == 1: + val = val & 0x1 + return oneto8[val] + elif nbits == 2: + val = val & 0x3 + return twoto8[val] + elif nbits == 3: + val = val & 0x7 + return threeto8[val] + else: + return 0 + + +OpenGLViewParent = docview.ScrollView + + +class OpenGLView(OpenGLViewParent): + def PreCreateWindow(self, cc): + self.HookMessage(self.OnSize, win32con.WM_SIZE) + # An OpenGL window must be created with the following flags and must not + # include CS_PARENTDC for the class style. Refer to SetPixelFormat + # documentation in the "Comments" section for further information. + style = cc[5] + style = style | win32con.WS_CLIPSIBLINGS | win32con.WS_CLIPCHILDREN + cc = cc[0], cc[1], cc[2], cc[3], cc[4], style, cc[6], cc[7], cc[8] + cc = self._obj_.PreCreateWindow(cc) + return cc + + def OnSize(self, params): + lParam = params[3] + cx = win32api.LOWORD(lParam) + cy = win32api.HIWORD(lParam) + glViewport(0, 0, cx, cy) + + if self.oldrect[2] > cx or self.oldrect[3] > cy: + self.RedrawWindow() + + self.OnSizeChange(cx, cy) + + self.oldrect = self.oldrect[0], self.oldrect[1], cx, cy + + def OnInitialUpdate(self): + self.SetScaleToFitSize( + (100, 100) + ) # or SetScrollSizes() - A Pythonwin requirement + return self._obj_.OnInitialUpdate() + + # return rc + + def OnCreate(self, cs): + self.oldrect = self.GetClientRect() + self._InitContexts() + self.Init() + + def OnDestroy(self, msg): + self.Term() + self._DestroyContexts() + return OpenGLViewParent.OnDestroy(self, msg) + + def OnDraw(self, dc): + self.DrawScene() + + def OnEraseBkgnd(self, dc): + return 1 + + # The OpenGL helpers + def _SetupPixelFormat(self): + dc = self.dc.GetSafeHdc() + pfd = CreatePIXELFORMATDESCRIPTOR() + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER + pfd.iPixelType = PFD_TYPE_RGBA + pfd.cColorBits = 24 + pfd.cDepthBits = 32 + pfd.iLayerType = PFD_MAIN_PLANE + pixelformat = ChoosePixelFormat(dc, pfd) + SetPixelFormat(dc, pixelformat, pfd) + self._CreateRGBPalette() + + def _CreateRGBPalette(self): + dc = self.dc.GetSafeHdc() + n = GetPixelFormat(dc) + pfd = DescribePixelFormat(dc, n) + if pfd.dwFlags & PFD_NEED_PALETTE: + n = 1 << pfd.cColorBits + pal = [] + for i in range(n): + this = ( + ComponentFromIndex(i, pfd.cRedBits, pfd.cRedShift), + ComponentFromIndex(i, pfd.cGreenBits, pfd.cGreenShift), + ComponentFromIndex(i, pfd.cBlueBits, pfd.cBlueShift), + 0, + ) + pal.append(this) + hpal = win32ui.CreatePalette(pal) + self.dc.SelectPalette(hpal, 0) + self.dc.RealizePalette() + + def _InitContexts(self): + self.dc = self.GetDC() + self._SetupPixelFormat() + hrc = wglCreateContext(self.dc.GetSafeHdc()) + wglMakeCurrent(self.dc.GetSafeHdc(), hrc) + + def _DestroyContexts(self): + hrc = wglGetCurrentContext() + wglMakeCurrent(0, 0) + if hrc: + wglDeleteContext(hrc) + + # The methods to support OpenGL + def DrawScene(self): + assert 0, "You must override this method" + + def Init(self): + assert 0, "You must override this method" + + def OnSizeChange(self, cx, cy): + pass + + def Term(self): + pass + + +class TestView(OpenGLView): + def OnSizeChange(self, right, bottom): + glClearColor(0.0, 0.0, 0.0, 1.0) + glClearDepth(1.0) + glEnable(GL_DEPTH_TEST) + + glMatrixMode(GL_PROJECTION) + if bottom: + aspect = right / bottom + else: + aspect = 0 # When window created! + glLoadIdentity() + gluPerspective(45.0, aspect, 3.0, 7.0) + glMatrixMode(GL_MODELVIEW) + + near_plane = 3.0 + far_plane = 7.0 + maxObjectSize = 3.0 + self.radius = near_plane + maxObjectSize / 2.0 + + def Init(self): + pass + + def DrawScene(self): + glClearColor(0.0, 0.0, 0.0, 1.0) + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) + + glPushMatrix() + glTranslatef(0.0, 0.0, -self.radius) + + self._DrawCone() + + self._DrawPyramid() + + glPopMatrix() + glFinish() + + SwapBuffers(wglGetCurrentDC()) + + def _DrawCone(self): + glColor3f(0.0, 1.0, 0.0) + + glPushMatrix() + glTranslatef(-1.0, 0.0, 0.0) + quadObj = gluNewQuadric() + gluQuadricDrawStyle(quadObj, GLU_FILL) + gluQuadricNormals(quadObj, GLU_SMOOTH) + gluCylinder(quadObj, 1.0, 0.0, 1.0, 20, 10) + # gluDeleteQuadric(quadObj); + glPopMatrix() + + def _DrawPyramid(self): + glPushMatrix() + glTranslatef(1.0, 0.0, 0.0) + glBegin(GL_TRIANGLE_FAN) + glColor3f(1.0, 0.0, 0.0) + glVertex3f(0.0, 1.0, 0.0) + glColor3f(0.0, 1.0, 0.0) + glVertex3f(-1.0, 0.0, 0.0) + glColor3f(0.0, 0.0, 1.0) + glVertex3f(0.0, 0.0, 1.0) + glColor3f(0.0, 1.0, 0.0) + glVertex3f(1.0, 0.0, 0.0) + glEnd() + glPopMatrix() + + +class CubeView(OpenGLView): + def OnSizeChange(self, right, bottom): + glClearColor(0.0, 0.0, 0.0, 1.0) + glClearDepth(1.0) + glEnable(GL_DEPTH_TEST) + + glMatrixMode(GL_PROJECTION) + if bottom: + aspect = right / bottom + else: + aspect = 0 # When window created! + glLoadIdentity() + gluPerspective(45.0, aspect, 3.0, 7.0) + glMatrixMode(GL_MODELVIEW) + + near_plane = 3.0 + far_plane = 7.0 + maxObjectSize = 3.0 + self.radius = near_plane + maxObjectSize / 2.0 + + def Init(self): + self.busy = 0 + self.wAngleY = 10.0 + self.wAngleX = 1.0 + self.wAngleZ = 5.0 + self.timerid = timer.set_timer(150, self.OnTimer) + + def OnTimer(self, id, timeVal): + self.DrawScene() + + def Term(self): + timer.kill_timer(self.timerid) + + def DrawScene(self): + if self.busy: + return + self.busy = 1 + + glClearColor(0.0, 0.0, 0.0, 1.0) + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) + + glPushMatrix() + + glTranslatef(0.0, 0.0, -self.radius) + glRotatef(self.wAngleX, 1.0, 0.0, 0.0) + glRotatef(self.wAngleY, 0.0, 1.0, 0.0) + glRotatef(self.wAngleZ, 0.0, 0.0, 1.0) + + self.wAngleX = self.wAngleX + 1.0 + self.wAngleY = self.wAngleY + 10.0 + self.wAngleZ = self.wAngleZ + 5.0 + + glBegin(GL_QUAD_STRIP) + glColor3f(1.0, 0.0, 1.0) + glVertex3f(-0.5, 0.5, 0.5) + + glColor3f(1.0, 0.0, 0.0) + glVertex3f(-0.5, -0.5, 0.5) + + glColor3f(1.0, 1.0, 1.0) + glVertex3f(0.5, 0.5, 0.5) + + glColor3f(1.0, 1.0, 0.0) + glVertex3f(0.5, -0.5, 0.5) + + glColor3f(0.0, 1.0, 1.0) + glVertex3f(0.5, 0.5, -0.5) + + glColor3f(0.0, 1.0, 0.0) + glVertex3f(0.5, -0.5, -0.5) + + glColor3f(0.0, 0.0, 1.0) + glVertex3f(-0.5, 0.5, -0.5) + + glColor3f(0.0, 0.0, 0.0) + glVertex3f(-0.5, -0.5, -0.5) + + glColor3f(1.0, 0.0, 1.0) + glVertex3f(-0.5, 0.5, 0.5) + + glColor3f(1.0, 0.0, 0.0) + glVertex3f(-0.5, -0.5, 0.5) + + glEnd() + + glBegin(GL_QUADS) + glColor3f(1.0, 0.0, 1.0) + glVertex3f(-0.5, 0.5, 0.5) + + glColor3f(1.0, 1.0, 1.0) + glVertex3f(0.5, 0.5, 0.5) + + glColor3f(0.0, 1.0, 1.0) + glVertex3f(0.5, 0.5, -0.5) + + glColor3f(0.0, 0.0, 1.0) + glVertex3f(-0.5, 0.5, -0.5) + glEnd() + + glBegin(GL_QUADS) + glColor3f(1.0, 0.0, 0.0) + glVertex3f(-0.5, -0.5, 0.5) + + glColor3f(1.0, 1.0, 0.0) + glVertex3f(0.5, -0.5, 0.5) + + glColor3f(0.0, 1.0, 0.0) + glVertex3f(0.5, -0.5, -0.5) + + glColor3f(0.0, 0.0, 0.0) + glVertex3f(-0.5, -0.5, -0.5) + glEnd() + + glPopMatrix() + + glFinish() + SwapBuffers(wglGetCurrentDC()) + + self.busy = 0 + + +def test(): + template = docview.DocTemplate(None, None, None, CubeView) + # template = docview.DocTemplate(None, None, None, TestView ) + template.OpenDocumentFile(None) + + +if __name__ == "__main__": + test() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/Demos/progressbar.py b/myenv/Lib/site-packages/pythonwin/pywin/Demos/progressbar.py new file mode 100644 index 000000000..81cd7e381 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/Demos/progressbar.py @@ -0,0 +1,105 @@ +# +# Progress bar control example +# +# PyCProgressCtrl encapsulates the MFC CProgressCtrl class. To use it, +# you: +# +# - Create the control with win32ui.CreateProgressCtrl() +# - Create the control window with PyCProgressCtrl.CreateWindow() +# - Initialize the range if you want it to be other than (0, 100) using +# PyCProgressCtrl.SetRange() +# - Either: +# - Set the step size with PyCProgressCtrl.SetStep(), and +# - Increment using PyCProgressCtrl.StepIt() +# or: +# - Set the amount completed using PyCProgressCtrl.SetPos() +# +# Example and progress bar code courtesy of KDL Technologies, Ltd., Hong Kong SAR, China. +# + +import win32con +import win32ui +from pywin.mfc import dialog + + +def MakeDlgTemplate(): + style = ( + win32con.DS_MODALFRAME + | win32con.WS_POPUP + | win32con.WS_VISIBLE + | win32con.WS_CAPTION + | win32con.WS_SYSMENU + | win32con.DS_SETFONT + ) + cs = win32con.WS_CHILD | win32con.WS_VISIBLE + + w = 215 + h = 36 + + dlg = [ + [ + "Progress bar control example", + (0, 0, w, h), + style, + None, + (8, "MS Sans Serif"), + ], + ] + + s = win32con.WS_TABSTOP | cs + + dlg.append( + [ + 128, + "Tick", + win32con.IDOK, + (10, h - 18, 50, 14), + s | win32con.BS_DEFPUSHBUTTON, + ] + ) + + dlg.append( + [ + 128, + "Cancel", + win32con.IDCANCEL, + (w - 60, h - 18, 50, 14), + s | win32con.BS_PUSHBUTTON, + ] + ) + + return dlg + + +class TestDialog(dialog.Dialog): + def OnInitDialog(self): + rc = dialog.Dialog.OnInitDialog(self) + self.pbar = win32ui.CreateProgressCtrl() + self.pbar.CreateWindow( + win32con.WS_CHILD | win32con.WS_VISIBLE, (10, 10, 310, 24), self, 1001 + ) + # self.pbar.SetStep (5) + self.progress = 0 + self.pincr = 5 + return rc + + def OnOK(self): + # NB: StepIt wraps at the end if you increment past the upper limit! + # self.pbar.StepIt() + self.progress = self.progress + self.pincr + if self.progress > 100: + self.progress = 100 + if self.progress <= 100: + self.pbar.SetPos(self.progress) + + +def demo(modal=0): + d = TestDialog(MakeDlgTemplate()) + if modal: + d.DoModal() + else: + d.CreateWindow() + + +if __name__ == "__main__": + demo(1) diff --git a/myenv/Lib/site-packages/pythonwin/pywin/Demos/sliderdemo.py b/myenv/Lib/site-packages/pythonwin/pywin/Demos/sliderdemo.py new file mode 100644 index 000000000..9fc7b5705 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/Demos/sliderdemo.py @@ -0,0 +1,76 @@ +# sliderdemo.py +# Demo of the slider control courtesy of Mike Fletcher. + +import win32con +import win32ui +from pywin.mfc import dialog + + +class MyDialog(dialog.Dialog): + """ + Example using simple controls + """ + + _dialogstyle = ( + win32con.WS_MINIMIZEBOX + | win32con.WS_DLGFRAME + | win32con.DS_MODALFRAME + | win32con.WS_POPUP + | win32con.WS_VISIBLE + | win32con.WS_CAPTION + | win32con.WS_SYSMENU + | win32con.DS_SETFONT + ) + _buttonstyle = ( + win32con.BS_PUSHBUTTON + | win32con.WS_TABSTOP + | win32con.WS_CHILD + | win32con.WS_VISIBLE + ) + ### The static template, contains all "normal" dialog items + DIALOGTEMPLATE = [ + # the dialog itself is the first element in the template + ["Example slider", (0, 0, 50, 43), _dialogstyle, None, (8, "MS SansSerif")], + # rest of elements are the controls within the dialog + # standard "Close" button + [128, "Close", win32con.IDCANCEL, (0, 30, 50, 13), _buttonstyle], + ] + ### ID of the control to be created during dialog initialisation + IDC_SLIDER = 9500 + + def __init__(self): + dialog.Dialog.__init__(self, self.DIALOGTEMPLATE) + + def OnInitDialog(self): + rc = dialog.Dialog.OnInitDialog(self) + # now initialise your controls that you want to create + # programmatically, including those which are OLE controls + # those created directly by win32ui.Create* + # and your "custom controls" which are subclasses/whatever + win32ui.EnableControlContainer() + self.slider = win32ui.CreateSliderCtrl() + self.slider.CreateWindow( + win32con.WS_TABSTOP | win32con.WS_VISIBLE, + (0, 0, 100, 30), + self._obj_, + self.IDC_SLIDER, + ) + self.HookMessage(self.OnSliderMove, win32con.WM_HSCROLL) + return rc + + def OnSliderMove(self, params): + print("Slider moved") + + def OnCancel(self): + print("The slider control is at position", self.slider.GetPos()) + self._obj_.OnCancel() + + +### +def demo(): + dia = MyDialog() + dia.DoModal() + + +if __name__ == "__main__": + demo() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/Demos/splittst.py b/myenv/Lib/site-packages/pythonwin/pywin/Demos/splittst.py new file mode 100644 index 000000000..0114bf09a --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/Demos/splittst.py @@ -0,0 +1,79 @@ +import commctrl +import fontdemo +import win32ui +from pywin.mfc import docview, window + +# derive from CMDIChild. This does much work for us. + + +class SplitterFrame(window.MDIChildWnd): + def __init__(self): + # call base CreateFrame + self.images = None + window.MDIChildWnd.__init__(self) + + def OnCreateClient(self, cp, context): + splitter = win32ui.CreateSplitter() + doc = context.doc + frame_rect = self.GetWindowRect() + size = ((frame_rect[2] - frame_rect[0]), (frame_rect[3] - frame_rect[1]) // 2) + sub_size = (size[0] // 2, size[1]) + splitter.CreateStatic(self, 2, 1) + self.v1 = win32ui.CreateEditView(doc) + self.v2 = fontdemo.FontView(doc) + # CListControl view + self.v3 = win32ui.CreateListView(doc) + sub_splitter = win32ui.CreateSplitter() + # pass "splitter" so each view knows how to get to the others + sub_splitter.CreateStatic(splitter, 1, 2) + sub_splitter.CreateView(self.v1, 0, 0, (sub_size)) + sub_splitter.CreateView(self.v2, 0, 1, (0, 0)) # size ignored. + splitter.SetRowInfo(0, size[1], 0) + splitter.CreateView(self.v3, 1, 0, (0, 0)) # size ignored. + # Setup items in the imagelist + self.images = win32ui.CreateImageList(32, 32, 1, 5, 5) + self.images.Add(win32ui.GetApp().LoadIcon(win32ui.IDR_MAINFRAME)) + self.images.Add(win32ui.GetApp().LoadIcon(win32ui.IDR_PYTHONCONTYPE)) + self.images.Add(win32ui.GetApp().LoadIcon(win32ui.IDR_TEXTTYPE)) + self.v3.SetImageList(self.images, commctrl.LVSIL_NORMAL) + self.v3.InsertItem(0, "Icon 1", 0) + self.v3.InsertItem(0, "Icon 2", 1) + self.v3.InsertItem(0, "Icon 3", 2) + # self.v3.Arrange(commctrl.LVA_DEFAULT) Hmmm - win95 aligns left always??? + return 1 + + def OnDestroy(self, msg): + window.MDIChildWnd.OnDestroy(self, msg) + if self.images: + self.images.DeleteImageList() + self.images = None + + def InitialUpdateFrame(self, doc, makeVisible): + self.v1.ReplaceSel("Hello from Edit Window 1") + self.v1.SetModifiedFlag(0) + + +class SampleTemplate(docview.DocTemplate): + def __init__(self): + docview.DocTemplate.__init__( + self, win32ui.IDR_PYTHONTYPE, None, SplitterFrame, None + ) + + def InitialUpdateFrame(self, frame, doc, makeVisible): + # print "frame is ", frame, frame._obj_ + # print "doc is ", doc, doc._obj_ + self._obj_.InitialUpdateFrame(frame, doc, makeVisible) # call default handler. + frame.InitialUpdateFrame(doc, makeVisible) + + +def demo(): + template = SampleTemplate() + doc = template.OpenDocumentFile(None) + doc.SetTitle("Splitter Demo") + + +if __name__ == "__main__": + import demoutils + + if demoutils.NeedGoodGUI(): + demo() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/Demos/threadedgui.py b/myenv/Lib/site-packages/pythonwin/pywin/Demos/threadedgui.py new file mode 100644 index 000000000..bbe1369e4 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/Demos/threadedgui.py @@ -0,0 +1,189 @@ +# Demo of using just windows, without documents and views. + +# Also demo of a GUI thread, pretty much direct from the MFC C++ sample MTMDI. + +import timer +import win32api +import win32con +import win32ui +from pywin.mfc import docview, thread, window +from pywin.mfc.thread import WinThread + +WM_USER_PREPARE_TO_CLOSE = win32con.WM_USER + 32 + +# font is a dictionary in which the following elements matter: +# (the best matching font to supplied parameters is returned) +# name string name of the font as known by Windows +# size point size of font in logical units +# weight weight of font (win32con.FW_NORMAL, win32con.FW_BOLD) +# italic boolean; true if set to anything but None +# underline boolean; true if set to anything but None + + +# This window is a child window of a frame. It is not the frame window itself. +class FontWindow(window.Wnd): + def __init__(self, text="Python Rules!"): + window.Wnd.__init__(self) + self.text = text + self.index = 0 + self.incr = 1 + self.width = self.height = 0 + self.ChangeAttributes() + # set up message handlers + + def Create(self, title, style, rect, parent): + classStyle = win32con.CS_HREDRAW | win32con.CS_VREDRAW + className = win32ui.RegisterWndClass( + classStyle, 0, win32con.COLOR_WINDOW + 1, 0 + ) + self._obj_ = win32ui.CreateWnd() + self._obj_.AttachObject(self) + self._obj_.CreateWindow( + className, title, style, rect, parent, win32ui.AFX_IDW_PANE_FIRST + ) + self.HookMessage(self.OnSize, win32con.WM_SIZE) + self.HookMessage(self.OnPrepareToClose, WM_USER_PREPARE_TO_CLOSE) + self.HookMessage(self.OnDestroy, win32con.WM_DESTROY) + self.timerid = timer.set_timer(100, self.OnTimer) + self.InvalidateRect() + + def OnDestroy(self, msg): + timer.kill_timer(self.timerid) + + def OnTimer(self, id, timeVal): + self.index = self.index + self.incr + if self.index > len(self.text): + self.incr = -1 + self.index = len(self.text) + elif self.index < 0: + self.incr = 1 + self.index = 0 + self.InvalidateRect() + + def OnPaint(self): + # print "Paint message from thread", win32api.GetCurrentThreadId() + dc, paintStruct = self.BeginPaint() + self.OnPrepareDC(dc, None) + + if self.width == 0 and self.height == 0: + left, top, right, bottom = self.GetClientRect() + self.width = right - left + self.height = bottom - top + x, y = self.width // 2, self.height // 2 + dc.TextOut(x, y, self.text[: self.index]) + self.EndPaint(paintStruct) + + def ChangeAttributes(self): + font_spec = {"name": "Arial", "height": 42} + self.font = win32ui.CreateFont(font_spec) + + def OnPrepareToClose(self, params): + self.DestroyWindow() + + def OnSize(self, params): + lParam = params[3] + self.width = win32api.LOWORD(lParam) + self.height = win32api.HIWORD(lParam) + + def OnPrepareDC(self, dc, printinfo): + # Set up the DC for forthcoming OnDraw call + dc.SetTextColor(win32api.RGB(0, 0, 255)) + dc.SetBkColor(win32api.GetSysColor(win32con.COLOR_WINDOW)) + dc.SelectObject(self.font) + dc.SetTextAlign(win32con.TA_CENTER | win32con.TA_BASELINE) + + +class FontFrame(window.MDIChildWnd): + def __init__(self): + pass # Dont call base class doc/view version... + + def Create(self, title, rect=None, parent=None): + style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_OVERLAPPEDWINDOW + self._obj_ = win32ui.CreateMDIChild() + self._obj_.AttachObject(self) + + self._obj_.CreateWindow(None, title, style, rect, parent) + rect = self.GetClientRect() + rect = (0, 0, rect[2] - rect[0], rect[3] - rect[1]) + self.child = FontWindow("Not threaded") + self.child.Create( + "FontDemo", win32con.WS_CHILD | win32con.WS_VISIBLE, rect, self + ) + + +class TestThread(WinThread): + def __init__(self, parentWindow): + self.parentWindow = parentWindow + self.child = None + WinThread.__init__(self) + + def InitInstance(self): + rect = self.parentWindow.GetClientRect() + rect = (0, 0, rect[2] - rect[0], rect[3] - rect[1]) + + self.child = FontWindow() + self.child.Create( + "FontDemo", win32con.WS_CHILD | win32con.WS_VISIBLE, rect, self.parentWindow + ) + self.SetMainFrame(self.child) + return WinThread.InitInstance(self) + + def ExitInstance(self): + return 0 + + +class ThreadedFontFrame(window.MDIChildWnd): + def __init__(self): + pass # Dont call base class doc/view version... + self.thread = None + + def Create(self, title, rect=None, parent=None): + style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_OVERLAPPEDWINDOW + self._obj_ = win32ui.CreateMDIChild() + self._obj_.CreateWindow(None, title, style, rect, parent) + self._obj_.HookMessage(self.OnDestroy, win32con.WM_DESTROY) + self._obj_.HookMessage(self.OnSize, win32con.WM_SIZE) + + self.thread = TestThread(self) + self.thread.CreateThread() + + def OnSize(self, msg): + pass + + def OnDestroy(self, msg): + win32ui.OutputDebugString("OnDestroy\n") + if self.thread and self.thread.child: + child = self.thread.child + child.SendMessage(WM_USER_PREPARE_TO_CLOSE, 0, 0) + win32ui.OutputDebugString("Destroyed\n") + + +def Demo(): + f = FontFrame() + f.Create("Font Demo") + + +def ThreadedDemo(): + rect = win32ui.GetMainFrame().GetMDIClient().GetClientRect() + rect = rect[0], int(rect[3] * 3 / 4), int(rect[2] / 4), rect[3] + incr = rect[2] + for i in range(4): + if i == 0: + f = FontFrame() + title = "Not threaded" + else: + f = ThreadedFontFrame() + title = "Threaded GUI Demo" + f.Create(title, rect) + rect = rect[0] + incr, rect[1], rect[2] + incr, rect[3] + # Givem a chance to start + win32api.Sleep(100) + win32ui.PumpWaitingMessages() + + +if __name__ == "__main__": + import demoutils + + if demoutils.NeedGoodGUI(): + ThreadedDemo() +# Demo() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/Demos/toolbar.py b/myenv/Lib/site-packages/pythonwin/pywin/Demos/toolbar.py new file mode 100644 index 000000000..e56aefef1 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/Demos/toolbar.py @@ -0,0 +1,106 @@ +# Demo of ToolBars + +# Shows the toolbar control. +# Demos how to make custom tooltips, etc. + +import commctrl +import win32api +import win32con +import win32ui +from pywin.mfc import afxres, docview, window + + +class GenericFrame(window.MDIChildWnd): + def OnCreateClient(self, cp, context): + # handlers for toolbar buttons + self.HookCommand(self.OnPrevious, 401) + self.HookCommand(self.OnNext, 402) + # Its not necessary for us to hook both of these - the + # common controls should fall-back all by themselves. + # Indeed, given we hook TTN_NEEDTEXTW, commctrl.TTN_NEEDTEXTA + # will not be called. + self.HookNotify(self.GetTTText, commctrl.TTN_NEEDTEXT) + self.HookNotify(self.GetTTText, commctrl.TTN_NEEDTEXTW) + + # parent = win32ui.GetMainFrame() + parent = self + style = ( + win32con.WS_CHILD + | win32con.WS_VISIBLE + | afxres.CBRS_SIZE_DYNAMIC + | afxres.CBRS_TOP + | afxres.CBRS_TOOLTIPS + | afxres.CBRS_FLYBY + ) + + buttons = (win32ui.ID_APP_ABOUT, win32ui.ID_VIEW_INTERACTIVE) + bitmap = win32ui.IDB_BROWSER_HIER + tbid = 0xE840 + self.toolbar = tb = win32ui.CreateToolBar(parent, style, tbid) + tb.LoadBitmap(bitmap) + tb.SetButtons(buttons) + + tb.EnableDocking(afxres.CBRS_ALIGN_ANY) + tb.SetWindowText("Test") + parent.EnableDocking(afxres.CBRS_ALIGN_ANY) + parent.DockControlBar(tb) + parent.LoadBarState("ToolbarTest") + window.MDIChildWnd.OnCreateClient(self, cp, context) + return 1 + + def OnDestroy(self, msg): + self.SaveBarState("ToolbarTest") + + def GetTTText(self, std, extra): + (hwndFrom, idFrom, code) = std + text, hinst, flags = extra + if flags & commctrl.TTF_IDISHWND: + return # Not handled + if idFrom == win32ui.ID_APP_ABOUT: + # our 'extra' return value needs to be the following + # entries from a NMTTDISPINFO[W] struct: + # (szText, hinst, uFlags). None means 'don't change + # the value' + return 0, ("It works!", None, None) + return None # not handled. + + def GetMessageString(self, id): + if id == win32ui.ID_APP_ABOUT: + return "Dialog Test\nTest" + else: + return self._obj_.GetMessageString(id) + + def OnSize(self, params): + print("OnSize called with ", params) + + def OnNext(self, id, cmd): + print("OnNext called") + + def OnPrevious(self, id, cmd): + print("OnPrevious called") + + +msg = """\ +This toolbar was dynamically created.\r +\r +The first item's tooltips is provided by Python code.\r +\r +(Dont close the window with the toolbar in a floating state - it may not re-appear!)\r +""" + + +def test(): + template = docview.DocTemplate( + win32ui.IDR_PYTHONTYPE, None, GenericFrame, docview.EditView + ) + doc = template.OpenDocumentFile(None) + doc.SetTitle("Toolbar Test") + view = doc.GetFirstView() + view.SetWindowText(msg) + + +if __name__ == "__main__": + import demoutils + + if demoutils.NeedGoodGUI(): + test() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/IDLE.cfg b/myenv/Lib/site-packages/pythonwin/pywin/IDLE.cfg new file mode 100644 index 000000000..b1987b14c --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/IDLE.cfg @@ -0,0 +1,29 @@ +[General] +# We base this configuration on the default config. +# You can list "Based On" as many times as you like +Based On = default + +[Keys] +# Only list keys different to default. +# Note you may wish to rebind some of the default +# Pythonwin keys to "Beep" or "DoNothing" + +Alt+L = LocateSelectedFile +Ctrl+Q = AppExit + +# Other non-default Pythonwin keys +Alt+A = EditSelectAll +Alt+M = LocateModule + +# Movement +Ctrl+D = GotoEndOfFile + +# Tabs and other indent features +Alt+T = <> +Ctrl+[ = <> +Ctrl+] = <> + +[Keys:Interactive] +Alt+P = <> +Alt+N = <> + diff --git a/myenv/Lib/site-packages/pythonwin/pywin/__init__.py b/myenv/Lib/site-packages/pythonwin/pywin/__init__.py new file mode 100644 index 000000000..2e44fba5c --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/__init__.py @@ -0,0 +1,10 @@ +# is_platform_unicode is an old variable that was never correctly used and +# is no longer referenced in pywin32. It is staying for a few releases incase +# others are looking at it, but it will go away soon! +is_platform_unicode = 0 + +# Ditto default_platform_encoding - not referenced and will die. +default_platform_encoding = "mbcs" + +# This one *is* real and used - but in practice can't be changed. +default_scintilla_encoding = "utf-8" # Scintilla _only_ supports this ATM diff --git a/myenv/Lib/site-packages/pythonwin/pywin/debugger/__init__.py b/myenv/Lib/site-packages/pythonwin/pywin/debugger/__init__.py new file mode 100644 index 000000000..5f41960d3 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/debugger/__init__.py @@ -0,0 +1,139 @@ +import sys + + +# Some cruft to deal with the Pythonwin GUI booting up from a non GUI app. +def _MakeDebuggerGUI(): + app.InitInstance() + + +isInprocApp = -1 + + +def _CheckNeedGUI(): + global isInprocApp + if isInprocApp == -1: + import win32ui + + isInprocApp = win32ui.GetApp().IsInproc() + if isInprocApp: + # MAY Need it - may already have one + need = "pywin.framework.app" not in sys.modules + else: + need = 0 + if need: + import pywin.framework.app + + from . import dbgpyapp + + pywin.framework.app.CreateDefaultGUI(dbgpyapp.DebuggerPythonApp) + + else: + # Check we have the appropriate editor + # No longer necessary! + pass + return need + + +# Inject some methods in the top level name-space. +currentDebugger = None # Wipe out any old one on reload. + + +def _GetCurrentDebugger(): + global currentDebugger + if currentDebugger is None: + _CheckNeedGUI() + from . import debugger + + currentDebugger = debugger.Debugger() + return currentDebugger + + +def GetDebugger(): + # An error here is not nice - as we are probably trying to + # break into the debugger on a Python error, any + # error raised by this is usually silent, and causes + # big problems later! + try: + rc = _GetCurrentDebugger() + rc.GUICheckInit() + return rc + except: + print("Could not create the debugger!") + import traceback + + traceback.print_exc() + return None + + +def close(): + if currentDebugger is not None: + currentDebugger.close() + + +def run(cmd, globals=None, locals=None, start_stepping=1): + _GetCurrentDebugger().run(cmd, globals, locals, start_stepping) + + +def runeval(expression, globals=None, locals=None): + return _GetCurrentDebugger().runeval(expression, globals, locals) + + +def runcall(*args): + return _GetCurrentDebugger().runcall(*args) + + +def set_trace(): + import sys + + d = _GetCurrentDebugger() + + if d.frameShutdown: + return # App closing + + if d.stopframe != d.botframe: + # If im not "running" + return + + sys.settrace(None) # May be hooked + d.reset() + d.set_trace() + + +# "brk" is an alias for "set_trace" ("break" is a reserved word :-( +brk = set_trace + +# Post-Mortem interface + + +def post_mortem(t=None): + if t is None: + t = sys.exc_info()[2] # Will be valid if we are called from an except handler. + if t is None: + try: + t = sys.last_traceback + except AttributeError: + print( + "No traceback can be found from which to perform post-mortem debugging!" + ) + print("No debugging can continue") + return + p = _GetCurrentDebugger() + if p.frameShutdown: + return # App closing + # No idea why I need to settrace to None - it should have been reset by now? + sys.settrace(None) + p.reset() + while t.tb_next != None: + t = t.tb_next + p.bAtPostMortem = 1 + p.prep_run(None) + try: + p.interaction(t.tb_frame, t) + finally: + t = None + p.bAtPostMortem = 0 + p.done_run() + + +def pm(t=None): + post_mortem(t) diff --git a/myenv/Lib/site-packages/pythonwin/pywin/debugger/configui.py b/myenv/Lib/site-packages/pythonwin/pywin/debugger/configui.py new file mode 100644 index 000000000..32de4ee5f --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/debugger/configui.py @@ -0,0 +1,34 @@ +import win32ui +from pywin.mfc import dialog + +from . import dbgcon + + +class DebuggerOptionsPropPage(dialog.PropertyPage): + def __init__(self): + dialog.PropertyPage.__init__(self, win32ui.IDD_PP_DEBUGGER) + + def OnInitDialog(self): + options = self.options = dbgcon.LoadDebuggerOptions() + self.AddDDX(win32ui.IDC_CHECK1, dbgcon.OPT_HIDE) + self[dbgcon.OPT_STOP_EXCEPTIONS] = options[dbgcon.OPT_STOP_EXCEPTIONS] + self.AddDDX(win32ui.IDC_CHECK2, dbgcon.OPT_STOP_EXCEPTIONS) + self[dbgcon.OPT_HIDE] = options[dbgcon.OPT_HIDE] + return dialog.PropertyPage.OnInitDialog(self) + + def OnOK(self): + self.UpdateData() + dirty = 0 + for key, val in list(self.items()): + if key in self.options: + if self.options[key] != val: + self.options[key] = val + dirty = 1 + if dirty: + dbgcon.SaveDebuggerOptions(self.options) + # If there is a debugger open, then set its options. + import pywin.debugger + + if pywin.debugger.currentDebugger is not None: + pywin.debugger.currentDebugger.options = self.options + return 1 diff --git a/myenv/Lib/site-packages/pythonwin/pywin/debugger/dbgcon.py b/myenv/Lib/site-packages/pythonwin/pywin/debugger/dbgcon.py new file mode 100644 index 000000000..402ec64b3 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/debugger/dbgcon.py @@ -0,0 +1,32 @@ +# General constants for the debugger + +DBGSTATE_NOT_DEBUGGING = 0 +DBGSTATE_RUNNING = 1 +DBGSTATE_BREAK = 2 +DBGSTATE_QUITTING = 3 # Attempting to back out of the debug session. + +LINESTATE_CURRENT = 0x1 # This line is where we are stopped +LINESTATE_BREAKPOINT = 0x2 # This line is a breakpoint +LINESTATE_CALLSTACK = 0x4 # This line is in the callstack. + +OPT_HIDE = "hide" +OPT_STOP_EXCEPTIONS = "stopatexceptions" + +import win32api +import win32ui + + +def DoGetOption(optsDict, optName, default): + optsDict[optName] = win32ui.GetProfileVal("Debugger Options", optName, default) + + +def LoadDebuggerOptions(): + opts = {} + DoGetOption(opts, OPT_HIDE, 0) + DoGetOption(opts, OPT_STOP_EXCEPTIONS, 1) + return opts + + +def SaveDebuggerOptions(opts): + for key, val in opts.items(): + win32ui.WriteProfileVal("Debugger Options", key, val) diff --git a/myenv/Lib/site-packages/pythonwin/pywin/debugger/dbgpyapp.py b/myenv/Lib/site-packages/pythonwin/pywin/debugger/dbgpyapp.py new file mode 100644 index 000000000..207f40493 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/debugger/dbgpyapp.py @@ -0,0 +1,48 @@ +# dbgpyapp.py - Debugger Python application class +# +import sys + +import win32con +import win32ui +from pywin.framework import intpyapp + +version = "0.3.0" + + +class DebuggerPythonApp(intpyapp.InteractivePythonApp): + def LoadMainFrame(self): + "Create the main applications frame" + self.frame = self.CreateMainFrame() + self.SetMainFrame(self.frame) + self.frame.LoadFrame(win32ui.IDR_DEBUGGER, win32con.WS_OVERLAPPEDWINDOW) + self.frame.DragAcceptFiles() # we can accept these. + self.frame.ShowWindow(win32con.SW_HIDE) + self.frame.UpdateWindow() + + # but we do rehook, hooking the new code objects. + self.HookCommands() + + def InitInstance(self): + # Use a registry path of "Python\Pythonwin Debugger + win32ui.SetAppName(win32ui.LoadString(win32ui.IDR_DEBUGGER)) + win32ui.SetRegistryKey("Python %s" % (sys.winver,)) + # We _need_ the Scintilla color editor. + # (and we _always_ get it now :-) + + numMRU = win32ui.GetProfileVal("Settings", "Recent File List Size", 10) + win32ui.LoadStdProfileSettings(numMRU) + + self.LoadMainFrame() + + # Display the interactive window if the user wants it. + from pywin.framework import interact + + interact.CreateInteractiveWindowUserPreference() + + # Load the modules we use internally. + self.LoadSystemModules() + # Load additional module the user may want. + self.LoadUserModules() + + # win32ui.CreateDebuggerThread() + win32ui.EnableControlContainer() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/debugger/debugger.py b/myenv/Lib/site-packages/pythonwin/pywin/debugger/debugger.py new file mode 100644 index 000000000..4acf9bc4d --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/debugger/debugger.py @@ -0,0 +1,1105 @@ +# debugger.py + +# A debugger for Pythonwin. Built from pdb. + +# Mark Hammond (MHammond@skippinet.com.au) - Dec 94. + +# usage: +# >>> import pywin.debugger +# >>> pywin.debugger.GetDebugger().run("command") + +import bdb +import os +import pdb +import string +import sys +import traceback +import types + +import commctrl +import pywin.docking.DockingBar +import win32api +import win32con +import win32ui +from pywin.framework import app, editor, interact, scriptutils +from pywin.framework.editor.color.coloreditor import MARKER_BREAKPOINT, MARKER_CURRENT +from pywin.mfc import afxres, dialog, object, window +from pywin.tools import browser, hierlist + +# import win32traceutil +if win32ui.UNICODE: + LVN_ENDLABELEDIT = commctrl.LVN_ENDLABELEDITW +else: + LVN_ENDLABELEDIT = commctrl.LVN_ENDLABELEDITA + +from .dbgcon import * + +error = "pywin.debugger.error" + + +def SetInteractiveContext(globs, locs): + if interact.edit is not None and interact.edit.currentView is not None: + interact.edit.currentView.SetContext(globs, locs) + + +def _LineStateToMarker(ls): + if ls == LINESTATE_CURRENT: + return MARKER_CURRENT + # elif ls == LINESTATE_CALLSTACK: + # return MARKER_CALLSTACK + return MARKER_BREAKPOINT + + +class HierListItem(browser.HLIPythonObject): + pass + + +class HierFrameItem(HierListItem): + def __init__(self, frame, debugger): + HierListItem.__init__(self, frame, repr(frame)) + self.debugger = debugger + + def GetText(self): + name = self.myobject.f_code.co_name + if not name or name == "?": + # See if locals has a '__name__' (ie, a module) + if "__name__" in self.myobject.f_locals: + name = str(self.myobject.f_locals["__name__"]) + " module" + else: + name = "" + + return "%s (%s:%d)" % ( + name, + os.path.split(self.myobject.f_code.co_filename)[1], + self.myobject.f_lineno, + ) + + def GetBitmapColumn(self): + if self.debugger.curframe is self.myobject: + return 7 + else: + return 8 + + def GetSubList(self): + ret = [] + ret.append(HierFrameDict(self.myobject.f_locals, "Locals", 2)) + ret.append(HierFrameDict(self.myobject.f_globals, "Globals", 1)) + return ret + + def IsExpandable(self): + return 1 + + def TakeDefaultAction(self): + # Set the default frame to be this frame. + self.debugger.set_cur_frame(self.myobject) + return 1 + + +class HierFrameDict(browser.HLIDict): + def __init__(self, dict, name, bitmapColumn): + self.bitmapColumn = bitmapColumn + browser.HLIDict.__init__(self, dict, name) + + def GetBitmapColumn(self): + return self.bitmapColumn + + +class NoStackAvailableItem(HierListItem): + def __init__(self, why): + HierListItem.__init__(self, None, why) + + def IsExpandable(self): + return 0 + + def GetText(self): + return self.name + + def GetBitmapColumn(self): + return 8 + + +class HierStackRoot(HierListItem): + def __init__(self, debugger): + HierListItem.__init__(self, debugger, None) + self.last_stack = [] + + ## def __del__(self): + ## print "HierStackRoot dieing" + def GetSubList(self): + debugger = self.myobject + # print self.debugger.stack, self.debugger.curframe + ret = [] + if debugger.debuggerState == DBGSTATE_BREAK: + stackUse = debugger.stack[:] + stackUse.reverse() + self.last_stack = [] + for frame, lineno in stackUse: + self.last_stack.append((frame, lineno)) + if ( + frame is debugger.userbotframe + ): # Dont bother showing frames below our bottom frame. + break + for frame, lineno in self.last_stack: + ret.append(HierFrameItem(frame, debugger)) + ## elif debugger.debuggerState==DBGSTATE_NOT_DEBUGGING: + ## ret.append(NoStackAvailableItem('')) + ## else: + ## ret.append(NoStackAvailableItem('')) + return ret + + def GetText(self): + return "root item" + + def IsExpandable(self): + return 1 + + +class HierListDebugger(hierlist.HierListWithItems): + """Hier List of stack frames, breakpoints, whatever""" + + def __init__(self): + hierlist.HierListWithItems.__init__( + self, None, win32ui.IDB_DEBUGGER_HIER, None, win32api.RGB(255, 0, 0) + ) + + def Setup(self, debugger): + root = HierStackRoot(debugger) + self.AcceptRoot(root) + + +# def Refresh(self): +# self.Setup() + + +class DebuggerWindow(window.Wnd): + def __init__(self, ob): + window.Wnd.__init__(self, ob) + self.debugger = None + + def Init(self, debugger): + self.debugger = debugger + + def GetDefRect(self): + defRect = app.LoadWindowSize("Debugger Windows\\" + self.title) + if defRect[2] - defRect[0] == 0: + defRect = 0, 0, 150, 150 + return defRect + + def OnDestroy(self, msg): + newSize = self.GetWindowPlacement()[4] + pywin.framework.app.SaveWindowSize("Debugger Windows\\" + self.title, newSize) + return window.Wnd.OnDestroy(self, msg) + + def OnKeyDown(self, msg): + key = msg[2] + if key in (13, 27, 32): + return 1 + if key in (46, 8): # delete/BS key + self.DeleteSelected() + return 0 + view = scriptutils.GetActiveView() + try: + firer = view.bindings.fire_key_event + except AttributeError: + firer = None + if firer is not None: + return firer(msg) + else: + return 1 + + def DeleteSelected(self): + win32api.MessageBeep() + + def EditSelected(self): + win32api.MessageBeep() + + +class DebuggerStackWindow(DebuggerWindow): + title = "Stack" + + def __init__(self): + DebuggerWindow.__init__(self, win32ui.CreateTreeCtrl()) + self.list = HierListDebugger() + self.listOK = 0 + + def SaveState(self): + self.list.DeleteAllItems() + self.listOK = 0 + win32ui.WriteProfileVal( + "Debugger Windows\\" + self.title, "Visible", self.IsWindowVisible() + ) + + def CreateWindow(self, parent): + style = ( + win32con.WS_CHILD + | win32con.WS_VISIBLE + | win32con.WS_BORDER + | commctrl.TVS_HASLINES + | commctrl.TVS_LINESATROOT + | commctrl.TVS_HASBUTTONS + ) + self._obj_.CreateWindow(style, self.GetDefRect(), parent, win32ui.IDC_LIST1) + self.HookMessage(self.OnKeyDown, win32con.WM_KEYDOWN) + self.HookMessage(self.OnKeyDown, win32con.WM_SYSKEYDOWN) + self.list.HierInit(parent, self) + self.listOK = 0 # delayed setup + # self.list.Setup() + + def RespondDebuggerState(self, state): + assert self.debugger is not None, "Init not called" + if not self.listOK: + self.listOK = 1 + self.list.Setup(self.debugger) + else: + self.list.Refresh() + + def RespondDebuggerData(self): + try: + handle = self.GetChildItem(0) + except win32ui.error: + return # No items + while 1: + item = self.list.ItemFromHandle(handle) + col = self.list.GetBitmapColumn(item) + selCol = self.list.GetSelectedBitmapColumn(item) + if selCol is None: + selCol = col + if self.list.GetItemImage(handle) != (col, selCol): + self.list.SetItemImage(handle, col, selCol) + try: + handle = self.GetNextSiblingItem(handle) + except win32ui.error: + break + + +class DebuggerListViewWindow(DebuggerWindow): + def __init__(self): + DebuggerWindow.__init__(self, win32ui.CreateListCtrl()) + + def CreateWindow(self, parent): + list = self + style = ( + win32con.WS_CHILD + | win32con.WS_VISIBLE + | win32con.WS_BORDER + | commctrl.LVS_EDITLABELS + | commctrl.LVS_REPORT + ) + self._obj_.CreateWindow(style, self.GetDefRect(), parent, win32ui.IDC_LIST1) + self.HookMessage(self.OnKeyDown, win32con.WM_KEYDOWN) + self.HookMessage(self.OnKeyDown, win32con.WM_SYSKEYDOWN) + list = self + title, width = self.columns[0] + itemDetails = (commctrl.LVCFMT_LEFT, width, title, 0) + list.InsertColumn(0, itemDetails) + col = 1 + for title, width in self.columns[1:]: + col = col + 1 + itemDetails = (commctrl.LVCFMT_LEFT, width, title, 0) + list.InsertColumn(col, itemDetails) + parent.HookNotify(self.OnListEndLabelEdit, LVN_ENDLABELEDIT) + parent.HookNotify(self.OnItemRightClick, commctrl.NM_RCLICK) + parent.HookNotify(self.OnItemDoubleClick, commctrl.NM_DBLCLK) + + def RespondDebuggerData(self): + pass + + def RespondDebuggerState(self, state): + pass + + def EditSelected(self): + try: + sel = self.GetNextItem(-1, commctrl.LVNI_SELECTED) + except win32ui.error: + return + self.EditLabel(sel) + + def OnKeyDown(self, msg): + key = msg[2] + # If someone starts typing, they probably are trying to edit the text! + if chr(key) in string.ascii_uppercase: + self.EditSelected() + return 0 + return DebuggerWindow.OnKeyDown(self, msg) + + def OnItemDoubleClick(self, notify_data, extra): + self.EditSelected() + + def OnItemRightClick(self, notify_data, extra): + # First select the item we right-clicked on. + pt = self.ScreenToClient(win32api.GetCursorPos()) + flags, hItem, subitem = self.HitTest(pt) + if hItem == -1 or commctrl.TVHT_ONITEM & flags == 0: + return None + self.SetItemState(hItem, commctrl.LVIS_SELECTED, commctrl.LVIS_SELECTED) + + menu = win32ui.CreatePopupMenu() + menu.AppendMenu(win32con.MF_STRING | win32con.MF_ENABLED, 1000, "Edit item") + menu.AppendMenu(win32con.MF_STRING | win32con.MF_ENABLED, 1001, "Delete item") + dockbar = self.GetParent() + if dockbar.IsFloating(): + hook_parent = win32ui.GetMainFrame() + else: + hook_parent = self.GetParentFrame() + hook_parent.HookCommand(self.OnEditItem, 1000) + hook_parent.HookCommand(self.OnDeleteItem, 1001) + menu.TrackPopupMenu(win32api.GetCursorPos()) # track at mouse position. + return None + + def OnDeleteItem(self, command, code): + self.DeleteSelected() + + def OnEditItem(self, command, code): + self.EditSelected() + + +class DebuggerBreakpointsWindow(DebuggerListViewWindow): + title = "Breakpoints" + columns = [("Condition", 70), ("Location", 1024)] + + def SaveState(self): + items = [] + for i in range(self.GetItemCount()): + items.append(self.GetItemText(i, 0)) + items.append(self.GetItemText(i, 1)) + win32ui.WriteProfileVal( + "Debugger Windows\\" + self.title, "BreakpointList", "\t".join(items) + ) + win32ui.WriteProfileVal( + "Debugger Windows\\" + self.title, "Visible", self.IsWindowVisible() + ) + return 1 + + def OnListEndLabelEdit(self, std, extra): + item = extra[0] + text = item[4] + if text is None: + return + + item_id = self.GetItem(item[0])[6] + + from bdb import Breakpoint + + for bplist in Breakpoint.bplist.values(): + for bp in bplist: + if id(bp) == item_id: + if text.strip().lower() == "none": + text = None + bp.cond = text + break + self.RespondDebuggerData() + + def DeleteSelected(self): + try: + num = self.GetNextItem(-1, commctrl.LVNI_SELECTED) + item_id = self.GetItem(num)[6] + from bdb import Breakpoint + + for bplist in list(Breakpoint.bplist.values()): + for bp in bplist: + if id(bp) == item_id: + self.debugger.clear_break(bp.file, bp.line) + break + except win32ui.error: + win32api.MessageBeep() + self.RespondDebuggerData() + + def RespondDebuggerData(self): + l = self + l.DeleteAllItems() + index = -1 + from bdb import Breakpoint + + for bplist in Breakpoint.bplist.values(): + for bp in bplist: + baseName = os.path.split(bp.file)[1] + cond = bp.cond + item = index + 1, 0, 0, 0, str(cond), 0, id(bp) + index = l.InsertItem(item) + l.SetItemText(index, 1, "%s: %s" % (baseName, bp.line)) + + +class DebuggerWatchWindow(DebuggerListViewWindow): + title = "Watch" + columns = [("Expression", 70), ("Value", 1024)] + + def CreateWindow(self, parent): + DebuggerListViewWindow.CreateWindow(self, parent) + items = win32ui.GetProfileVal( + "Debugger Windows\\" + self.title, "Items", "" + ).split("\t") + index = -1 + for item in items: + if item: + index = self.InsertItem(index + 1, item) + self.InsertItem(index + 1, "") + + def SaveState(self): + items = [] + for i in range(self.GetItemCount() - 1): + items.append(self.GetItemText(i, 0)) + win32ui.WriteProfileVal( + "Debugger Windows\\" + self.title, "Items", "\t".join(items) + ) + win32ui.WriteProfileVal( + "Debugger Windows\\" + self.title, "Visible", self.IsWindowVisible() + ) + return 1 + + def OnListEndLabelEdit(self, std, extra): + item = extra[0] + itemno = item[0] + text = item[4] + if text is None: + return + self.SetItemText(itemno, 0, text) + if itemno == self.GetItemCount() - 1: + self.InsertItem(itemno + 1, "") + self.RespondDebuggerState(self.debugger.debuggerState) + + def DeleteSelected(self): + try: + num = self.GetNextItem(-1, commctrl.LVNI_SELECTED) + if num < self.GetItemCount() - 1: # We cant delete the last + self.DeleteItem(num) + except win32ui.error: + win32api.MessageBeep() + + def RespondDebuggerState(self, state): + globs = locs = None + if state == DBGSTATE_BREAK: + if self.debugger.curframe: + globs = self.debugger.curframe.f_globals + locs = self.debugger.curframe.f_locals + elif state == DBGSTATE_NOT_DEBUGGING: + import __main__ + + globs = locs = __main__.__dict__ + for i in range(self.GetItemCount() - 1): + text = self.GetItemText(i, 0) + if globs is None: + val = "" + else: + try: + val = repr(eval(text, globs, locs)) + except SyntaxError: + val = "Syntax Error" + except: + t, v, tb = sys.exc_info() + val = traceback.format_exception_only(t, v)[0].strip() + tb = None # prevent a cycle. + self.SetItemText(i, 1, val) + + +def CreateDebuggerDialog(parent, klass): + control = klass() + control.CreateWindow(parent) + return control + + +DebuggerDialogInfos = ( + (0xE810, DebuggerStackWindow, None), + (0xE811, DebuggerBreakpointsWindow, (10, 10)), + (0xE812, DebuggerWatchWindow, None), +) + + +# Prepare all the "control bars" for this package. +# If control bars are not all loaded when the toolbar-state functions are +# called, things go horribly wrong. +def PrepareControlBars(frame): + style = ( + win32con.WS_CHILD + | afxres.CBRS_SIZE_DYNAMIC + | afxres.CBRS_TOP + | afxres.CBRS_TOOLTIPS + | afxres.CBRS_FLYBY + ) + tbd = win32ui.CreateToolBar(frame, style, win32ui.ID_VIEW_TOOLBAR_DBG) + tbd.ModifyStyle(0, commctrl.TBSTYLE_FLAT) + tbd.LoadToolBar(win32ui.IDR_DEBUGGER) + tbd.EnableDocking(afxres.CBRS_ALIGN_ANY) + tbd.SetWindowText("Debugger") + frame.DockControlBar(tbd) + + # and the other windows. + for id, klass, float in DebuggerDialogInfos: + try: + frame.GetControlBar(id) + exists = 1 + except win32ui.error: + exists = 0 + if exists: + continue + bar = pywin.docking.DockingBar.DockingBar() + style = win32con.WS_CHILD | afxres.CBRS_LEFT # don't create visible. + bar.CreateWindow( + frame, + CreateDebuggerDialog, + klass.title, + id, + style, + childCreatorArgs=(klass,), + ) + bar.SetBarStyle( + bar.GetBarStyle() + | afxres.CBRS_TOOLTIPS + | afxres.CBRS_FLYBY + | afxres.CBRS_SIZE_DYNAMIC + ) + bar.EnableDocking(afxres.CBRS_ALIGN_ANY) + if float is None: + frame.DockControlBar(bar) + else: + frame.FloatControlBar(bar, float, afxres.CBRS_ALIGN_ANY) + + ## frame.ShowControlBar(bar, 0, 1) + + +SKIP_NONE = 0 +SKIP_STEP = 1 +SKIP_RUN = 2 + +debugger_parent = pdb.Pdb + + +class Debugger(debugger_parent): + def __init__(self): + self.inited = 0 + self.skipBotFrame = SKIP_NONE + self.userbotframe = None + self.frameShutdown = 0 + self.pumping = 0 + self.debuggerState = DBGSTATE_NOT_DEBUGGING # Assume so, anyway. + self.shownLineCurrent = None # The last filename I highlighted. + self.shownLineCallstack = None # The last filename I highlighted. + self.last_cmd_debugged = "" + self.abortClosed = 0 + self.isInitialBreakpoint = 0 + debugger_parent.__init__(self) + + # See if any break-points have been set in the editor + for doc in editor.editorTemplate.GetDocumentList(): + lineNo = -1 + while 1: + lineNo = doc.MarkerGetNext(lineNo + 1, MARKER_BREAKPOINT) + if lineNo <= 0: + break + self.set_break(doc.GetPathName(), lineNo) + + self.reset() + self.inForcedGUI = win32ui.GetApp().IsInproc() + self.options = LoadDebuggerOptions() + self.bAtException = self.bAtPostMortem = 0 + + def __del__(self): + self.close() + + def close(self, frameShutdown=0): + # abortClose indicates if we have total shutdown + # (ie, main window is dieing) + if self.pumping: + # Can stop pump here, as it only posts a message, and + # returns immediately. + if not self.StopDebuggerPump(): # User cancelled close. + return 0 + # NOTE - from this point on the close can not be + # stopped - the WM_QUIT message is already in the queue. + self.frameShutdown = frameShutdown + if not self.inited: + return 1 + self.inited = 0 + + SetInteractiveContext(None, None) + + frame = win32ui.GetMainFrame() + # Hide the debuger toolbars (as they wont normally form part of the main toolbar state. + for id, klass, float in DebuggerDialogInfos: + try: + tb = frame.GetControlBar(id) + if tb.dialog is not None: # We may never have actually been shown. + tb.dialog.SaveState() + frame.ShowControlBar(tb, 0, 1) + except win32ui.error: + pass + + self._UnshowCurrentLine() + self.set_quit() + return 1 + + def StopDebuggerPump(self): + assert self.pumping, "Can't stop the debugger pump if Im not pumping!" + # After stopping a pump, I may never return. + if self.GUIAboutToFinishInteract(): + self.pumping = 0 + win32ui.StopDebuggerPump() # Posts a message, so we do return. + return 1 + return 0 + + def get_option(self, option): + """Public interface into debugger options""" + try: + return self.options[option] + except KeyError: + raise error("Option %s is not a valid option" % option) + + def prep_run(self, cmd): + pass + + def done_run(self, cmd=None): + self.RespondDebuggerState(DBGSTATE_NOT_DEBUGGING) + self.close() + + def canonic(self, fname): + return os.path.abspath(fname).lower() + + def reset(self): + debugger_parent.reset(self) + self.userbotframe = None + self.UpdateAllLineStates() + self._UnshowCurrentLine() + + def setup(self, f, t): + debugger_parent.setup(self, f, t) + self.bAtException = t is not None + + def set_break(self, filename, lineno, temporary=0, cond=None): + filename = self.canonic(filename) + self.SetLineState(filename, lineno, LINESTATE_BREAKPOINT) + return debugger_parent.set_break(self, filename, lineno, temporary, cond) + + def clear_break(self, filename, lineno): + filename = self.canonic(filename) + self.ResetLineState(filename, lineno, LINESTATE_BREAKPOINT) + return debugger_parent.clear_break(self, filename, lineno) + + def cmdloop(self): + if self.frameShutdown: + return # App in the process of closing - never break in! + self.GUIAboutToBreak() + + def print_stack_entry(self, frame): + # We dont want a stack printed - our GUI is better :-) + pass + + def user_return(self, frame, return_value): + # Same as parent, just no "print" + # This function is called when a return trap is set here + frame.f_locals["__return__"] = return_value + self.interaction(frame, None) + + def user_call(self, frame, args): + # base class has an annoying 'print' that adds no value to us... + if self.stop_here(frame): + self.interaction(frame, None) + + def user_exception(self, frame, exc_info): + # This function is called if an exception occurs, + # but only if we are to stop at or just below this level + (exc_type, exc_value, exc_traceback) = exc_info + if self.get_option(OPT_STOP_EXCEPTIONS): + frame.f_locals["__exception__"] = exc_type, exc_value + print("Unhandled exception while debugging...") + # on both py2k and py3k, we may be called with exc_value + # being the args to the exception, or it may already be + # instantiated (IOW, PyErr_Normalize() hasn't been + # called on the args). In py2k this is fine, but in + # py3k, traceback.print_exception fails. So on py3k + # we instantiate an exception instance to print. + if sys.version_info > (3,) and not isinstance(exc_value, BaseException): + # they are args - may be a single item or already a tuple + if not isinstance(exc_value, tuple): + exc_value = (exc_value,) + exc_value = exc_type(*exc_value) + + traceback.print_exception(exc_type, exc_value, exc_traceback) + self.interaction(frame, exc_traceback) + + def user_line(self, frame): + if frame.f_lineno == 0: + return + debugger_parent.user_line(self, frame) + + def stop_here(self, frame): + if self.isInitialBreakpoint: + self.isInitialBreakpoint = 0 + self.set_continue() + return 0 + if frame is self.botframe and self.skipBotFrame == SKIP_RUN: + self.set_continue() + return 0 + if frame is self.botframe and self.skipBotFrame == SKIP_STEP: + self.set_step() + return 0 + return debugger_parent.stop_here(self, frame) + + def run(self, cmd, globals=None, locals=None, start_stepping=1): + if not isinstance(cmd, (str, types.CodeType)): + raise TypeError("Only strings can be run") + self.last_cmd_debugged = cmd + if start_stepping: + self.isInitialBreakpoint = 0 + else: + self.isInitialBreakpoint = 1 + try: + if globals is None: + import __main__ + + globals = __main__.__dict__ + if locals is None: + locals = globals + self.reset() + self.prep_run(cmd) + sys.settrace(self.trace_dispatch) + if type(cmd) != types.CodeType: + cmd = cmd + "\n" + try: + try: + if start_stepping: + self.skipBotFrame = SKIP_STEP + else: + self.skipBotFrame = SKIP_RUN + exec(cmd, globals, locals) + except bdb.BdbQuit: + pass + finally: + self.skipBotFrame = SKIP_NONE + self.quitting = 1 + sys.settrace(None) + + finally: + self.done_run(cmd) + + def runeval(self, expr, globals=None, locals=None): + self.prep_run(expr) + try: + debugger_parent.runeval(self, expr, globals, locals) + finally: + self.done_run(expr) + + def runexec(self, what, globs=None, locs=None): + self.reset() + sys.settrace(self.trace_dispatch) + try: + try: + exec(what, globs, locs) + except bdb.BdbQuit: + pass + finally: + self.quitting = 1 + sys.settrace(None) + + def do_set_step(self): + if self.GUIAboutToRun(): + self.set_step() + + def do_set_next(self): + if self.GUIAboutToRun(): + self.set_next(self.curframe) + + def do_set_return(self): + if self.GUIAboutToRun(): + self.set_return(self.curframe) + + def do_set_continue(self): + if self.GUIAboutToRun(): + self.set_continue() + + def set_quit(self): + ok = 1 + if self.pumping: + ok = self.StopDebuggerPump() + if ok: + debugger_parent.set_quit(self) + + def _dump_frame_(self, frame, name=None): + if name is None: + name = "" + if frame: + if frame.f_code and frame.f_code.co_filename: + fname = os.path.split(frame.f_code.co_filename)[1] + else: + fname = "??" + print(repr(name), fname, frame.f_lineno, frame) + else: + print(repr(name), "None") + + def set_trace(self): + # Start debugging from _2_ levels up! + try: + 1 + "" + except: + frame = sys.exc_info()[2].tb_frame.f_back.f_back + self.reset() + self.userbotframe = None + while frame: + # scriptutils.py creates a local variable with name + # '_debugger_stop_frame_', and we dont go past it + # (everything above this is Pythonwin framework code) + if "_debugger_stop_frame_" in frame.f_locals: + self.userbotframe = frame + break + + frame.f_trace = self.trace_dispatch + self.botframe = frame + frame = frame.f_back + self.set_step() + sys.settrace(self.trace_dispatch) + + def set_cur_frame(self, frame): + # Sets the "current" frame - ie, the frame with focus. This is the + # frame on which "step out" etc actions are taken. + # This may or may not be the top of the stack. + assert frame is not None, "You must pass a valid frame" + self.curframe = frame + for f, index in self.stack: + if f is frame: + self.curindex = index + break + else: + assert 0, "Can't find the frame in the stack." + SetInteractiveContext(frame.f_globals, frame.f_locals) + self.GUIRespondDebuggerData() + self.ShowCurrentLine() + + def IsBreak(self): + return self.debuggerState == DBGSTATE_BREAK + + def IsDebugging(self): + return self.debuggerState != DBGSTATE_NOT_DEBUGGING + + def RespondDebuggerState(self, state): + if state == self.debuggerState: + return + if state == DBGSTATE_NOT_DEBUGGING: # Debugger exists, but not doing anything + title = "" + elif state == DBGSTATE_RUNNING: # Code is running under the debugger. + title = " - running" + elif state == DBGSTATE_BREAK: # We are at a breakpoint or stepping or whatever. + if self.bAtException: + if self.bAtPostMortem: + title = " - post mortem exception" + else: + title = " - exception" + else: + title = " - break" + else: + raise error("Invalid debugger state passed!") + win32ui.GetMainFrame().SetWindowText( + win32ui.LoadString(win32ui.IDR_MAINFRAME) + title + ) + if self.debuggerState == DBGSTATE_QUITTING and state != DBGSTATE_NOT_DEBUGGING: + print("Ignoring state change cos Im trying to stop!", state) + return + self.debuggerState = state + try: + frame = win32ui.GetMainFrame() + except win32ui.error: + frame = None + if frame is not None: + for id, klass, float in DebuggerDialogInfos: + cb = win32ui.GetMainFrame().GetControlBar(id).dialog + cb.RespondDebuggerState(state) + # Tell each open editor window about the state transition + for doc in editor.editorTemplate.GetDocumentList(): + doc.OnDebuggerStateChange(state) + self.ShowCurrentLine() + + # + # GUI debugger interface. + # + def GUICheckInit(self): + if self.inited: + return + self.inited = 1 + frame = win32ui.GetMainFrame() + + # Ensure the debugger windows are attached to the debugger. + for id, klass, float in DebuggerDialogInfos: + w = frame.GetControlBar(id) + w.dialog.Init(self) + # Show toolbar if it was visible during last debug session + # This would be better done using a CDockState, but that class is not wrapped yet + if win32ui.GetProfileVal( + "Debugger Windows\\" + w.dialog.title, "Visible", 0 + ): + frame.ShowControlBar(w, 1, 1) + + # ALWAYS show debugging toolbar, regardless of saved state + tb = frame.GetControlBar(win32ui.ID_VIEW_TOOLBAR_DBG) + frame.ShowControlBar(tb, 1, 1) + self.GUIRespondDebuggerData() + + # frame.RecalcLayout() + + def GetDebuggerBar(self, barName): + frame = win32ui.GetMainFrame() + for id, klass, float in DebuggerDialogInfos: + if klass.title == barName: + return frame.GetControlBar(id) + assert 0, "Can't find a bar of that name!" + + def GUIRespondDebuggerData(self): + if not self.inited: # GUI not inited - no toolbars etc. + return + + for id, klass, float in DebuggerDialogInfos: + cb = win32ui.GetMainFrame().GetControlBar(id).dialog + cb.RespondDebuggerData() + + def GUIAboutToRun(self): + if not self.StopDebuggerPump(): + return 0 + self._UnshowCurrentLine() + self.RespondDebuggerState(DBGSTATE_RUNNING) + SetInteractiveContext(None, None) + return 1 + + def GUIAboutToBreak(self): + "Called as the GUI debugger is about to get context, and take control of the running program." + self.GUICheckInit() + self.RespondDebuggerState(DBGSTATE_BREAK) + self.GUIAboutToInteract() + if self.pumping: + print("!!! Already pumping - outa here") + return + self.pumping = 1 + win32ui.StartDebuggerPump() # NOTE - This will NOT return until the user is finished interacting + assert not self.pumping, "Should not be pumping once the pump has finished" + if self.frameShutdown: # User shut down app while debugging + win32ui.GetMainFrame().PostMessage(win32con.WM_CLOSE) + + def GUIAboutToInteract(self): + "Called as the GUI is about to perform any interaction with the user" + frame = win32ui.GetMainFrame() + # Remember the enabled state of our main frame + # may be disabled primarily if a modal dialog is displayed. + # Only get at enabled via GetWindowLong. + self.bFrameEnabled = frame.IsWindowEnabled() + self.oldForeground = None + fw = win32ui.GetForegroundWindow() + if fw is not frame: + self.oldForeground = fw + # fw.EnableWindow(0) Leave enabled for now? + self.oldFrameEnableState = frame.IsWindowEnabled() + frame.EnableWindow(1) + if self.inForcedGUI and not frame.IsWindowVisible(): + frame.ShowWindow(win32con.SW_SHOW) + frame.UpdateWindow() + if self.curframe: + SetInteractiveContext(self.curframe.f_globals, self.curframe.f_locals) + else: + SetInteractiveContext(None, None) + self.GUIRespondDebuggerData() + + def GUIAboutToFinishInteract(self): + """Called as the GUI is about to finish any interaction with the user + Returns non zero if we are allowed to stop interacting""" + if self.oldForeground is not None: + try: + win32ui.GetMainFrame().EnableWindow(self.oldFrameEnableState) + self.oldForeground.EnableWindow(1) + except win32ui.error: + # old window may be dead. + pass + # self.oldForeground.SetForegroundWindow() - fails?? + if not self.inForcedGUI: + return 1 # Never a problem, and nothing else to do. + # If we are running a forced GUI, we may never get an opportunity + # to interact again. Therefore we perform a "SaveAll", to makesure that + # any documents are saved before leaving. + for template in win32ui.GetApp().GetDocTemplateList(): + for doc in template.GetDocumentList(): + if not doc.SaveModified(): + return 0 + # All documents saved - now hide the app and debugger. + if self.get_option(OPT_HIDE): + frame = win32ui.GetMainFrame() + frame.ShowWindow(win32con.SW_HIDE) + return 1 + + # + # Pythonwin interface - all stuff to do with showing source files, + # changing line states etc. + # + def ShowLineState(self, fileName, lineNo, lineState): + # Set the state of a line, open if not already + self.ShowLineNo(fileName, lineNo) + self.SetLineState(fileName, lineNo, lineState) + + def SetLineState(self, fileName, lineNo, lineState): + # Set the state of a line if the document is open. + doc = editor.editorTemplate.FindOpenDocument(fileName) + if doc is not None: + marker = _LineStateToMarker(lineState) + if not doc.MarkerCheck(lineNo, marker): + doc.MarkerAdd(lineNo, marker) + + def ResetLineState(self, fileName, lineNo, lineState): + # Set the state of a line if the document is open. + doc = editor.editorTemplate.FindOpenDocument(fileName) + if doc is not None: + marker = _LineStateToMarker(lineState) + doc.MarkerDelete(lineNo, marker) + + def UpdateDocumentLineStates(self, doc): + # Show all lines in their special status color. If the doc is open + # all line states are reset. + doc.MarkerDeleteAll(MARKER_BREAKPOINT) + doc.MarkerDeleteAll(MARKER_CURRENT) + fname = self.canonic(doc.GetPathName()) + # Now loop over all break-points + for line in self.breaks.get(fname, []): + doc.MarkerAdd(line, MARKER_BREAKPOINT) + # And the current line if in this document. + if self.shownLineCurrent and fname == self.shownLineCurrent[0]: + lineNo = self.shownLineCurrent[1] + if not doc.MarkerCheck(lineNo, MARKER_CURRENT): + doc.MarkerAdd(lineNo, MARKER_CURRENT) + + # if self.shownLineCallstack and fname == self.shownLineCallstack[0]: + # doc.MarkerAdd(self.shownLineCallstack[1], MARKER_CURRENT) + + def UpdateAllLineStates(self): + for doc in editor.editorTemplate.GetDocumentList(): + self.UpdateDocumentLineStates(doc) + + def ShowCurrentLine(self): + # Show the current line. Only ever 1 current line - undoes last current + # The "Current Line" is self.curframe. + # The "Callstack Line" is the top of the stack. + # If current == callstack, only show as current. + self._UnshowCurrentLine() # un-highlight the old one. + if self.curframe: + fileName = self.canonic(self.curframe.f_code.co_filename) + lineNo = self.curframe.f_lineno + self.shownLineCurrent = fileName, lineNo + self.ShowLineState(fileName, lineNo, LINESTATE_CURRENT) + + def _UnshowCurrentLine(self): + "Unshow the current line, and forget it" + if self.shownLineCurrent is not None: + fname, lineno = self.shownLineCurrent + self.ResetLineState(fname, lineno, LINESTATE_CURRENT) + self.shownLineCurrent = None + + def ShowLineNo(self, filename, lineno): + wasOpen = editor.editorTemplate.FindOpenDocument(filename) is not None + if os.path.isfile(filename) and scriptutils.JumpToDocument(filename, lineno): + if not wasOpen: + doc = editor.editorTemplate.FindOpenDocument(filename) + if doc is not None: + self.UpdateDocumentLineStates(doc) + return 1 + return 0 + return 1 + else: + # Can't find the source file - linecache may have it? + import linecache + + line = linecache.getline(filename, lineno) + print( + "%s(%d): %s" + % (os.path.basename(filename), lineno, line[:-1].expandtabs(4)) + ) + return 0 diff --git a/myenv/Lib/site-packages/pythonwin/pywin/debugger/fail.py b/myenv/Lib/site-packages/pythonwin/pywin/debugger/fail.py new file mode 100644 index 000000000..97721576c --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/debugger/fail.py @@ -0,0 +1,54 @@ +# NOTE NOTE - This module is designed to fail! +# +# The ONLY purpose for this script is testing/demoing the +# Pythonwin debugger package. + +# It does nothing useful, and it even doesnt do that! + +import sys +import time + +import pywin.debugger + + +def a(): + a = 1 + try: + b() + except: + # Break into the debugger with the exception information. + pywin.debugger.post_mortem(sys.exc_info()[2]) + a = 1 + a = 2 + a = 3 + a = 4 + + +def b(): + b = 1 + pywin.debugger.set_trace() + # After importing or running this module, you are likely to be + # sitting at the next line. This is because we explicitely + # broke into the debugger using the "set_trace() function + # "pywin.debugger.brk()" is a shorter alias for this. + c() + + +def c(): + c = 1 + d() + + +def d(): + d = 1 + e(d) + raise ValueError("Hi") + + +def e(arg): + e = 1 + time.sleep(1) + return e + + +a() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/default.cfg b/myenv/Lib/site-packages/pythonwin/pywin/default.cfg new file mode 100644 index 000000000..55371f6b2 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/default.cfg @@ -0,0 +1,215 @@ +# The default keyboard etc configuration file for Pythonwin. +# +# The format of this file is very similar to a Windows INI file. +# Sections are identified with [Section] lines, but comments +# use the standatd Python # character. Depending on the section, +# lines may not be in the standard "key=value" format. + +# NOTE: You should not need to modify this file. +# Simply create a new .CFG file, and add an entry: +# [General] +# BasedOn = Default +# +# and add your customisations. Then select your new configuration +# from the Pythonwin View/Options/Editor dialog. +# This way you get to add your own customisations, +# but still take advantage of changes to the default +# configuration in new releases. + +# See IDLE.cfg for an example extension configuration. +# +########################################################################## + +[IDLE Extensions] + +# The list of IDLE extensions to load. The extensions +# AutoIndent, AutoFormat and possibly others are +# "built-in", so do not need specifying. + +FormatParagraph +CallTips + + +[Keys] + +# The list of _default_ key definitions. +# See [Keys:Interactive] and [Keys:Editor] below for further defs. + +#Events of the format <> +# are events defined in IDLE extensions. + +Alt+Q = <> + +Ctrl+W = ViewWhitespace +Ctrl+Shift+8 = ViewWhitespace # The MSVC default key def. + +Ctrl+Shift+F = ViewFixedFont + +# Auto-complete, call-tips, etc. +Alt+/ = <> +Ctrl+Space = <> +( = <> +) = <> +Up = <> +Down = <> +Left = <> +Right = <> +. = KeyDot + +# Debugger - These are the MSVC default keys, for want of a better choice. +F9 = DbgBreakpointToggle +F5 = DbgGo +Shift+F5 = DbgClose +F11 = DbgStep +F10 = DbgStepOver +Shift+F11 = DbgStepOut + +Ctrl+F3 = AutoFindNext + + +[Keys:Editor] +# Key bindings specific to the editor +F2 = GotoNextBookmark +Ctrl+F2 = ToggleBookmark +Ctrl+G = GotoLine + +Alt+I = ShowInteractiveWindow +Alt-B = AddBanner # A sample Event defined in this file. + +# Block operations +Alt+3 = <> +Shift+Alt+3 = <> +Alt+4 = <> # IDLE default. +Alt+5 = <> +Alt+6 = <> + +# Tabs and other indent features +Back = <> +Ctrl+T = <> +Alt+U = <> +Enter = EnterKey +Tab = TabKey +Shift-Tab = <> + +# Folding +Add = FoldExpand +Alt+Add = FoldExpandAll +Shift+Add = FoldExpandSecondLevel +Subtract = FoldCollapse +Alt+Subtract = FoldCollapseAll +Shift+Subtract = FoldCollapseSecondLevel +Multiply = FoldTopLevel + +[Keys:Interactive] +# Key bindings specific to the interactive window. +# History for the interactive window +Ctrl+Up = <> +Ctrl+Down = <> +Enter = ProcessEnter +Ctrl+Enter = ProcessEnter +Shift+Enter = ProcessEnter +Esc = ProcessEsc +Alt+I = WindowBack # Toggle back to previous window. +Home = InteractiveHome # A sample Event defined in this file. +Shift+Home = InteractiveHomeExtend # A sample Event defined in this file. + +# When docked, the Ctrl+Tab and Shift+Ctrl+Tab keys dont work as expected. +Ctrl+Tab = MDINext +Ctrl+Shift+Tab = MDIPrev + +[Extensions] +# Python event handlers specific to this config file. +# All functions not starting with an "_" are assumed +# to be events, and take 2 params: +# * editor_window is the same object passed to IDLE +# extensions. editor_window.text is a text widget +# that conforms to the Tk text widget interface. +# * event is the event being fired. Will always be None +# in the current implementation. + +# Simply by defining these functions, they are available as +# events. +# Note that we bind keystrokes to these events in the various +# [Keys] sections. + +# Add a simple file/class/function simple banner +def AddBanner(editor_window, event): + + text = editor_window.text + big_line = "#" * 70 + banner = "%s\n## \n## \n## \n%s\n" % (big_line, big_line) + + # Insert at the start of the current line. + pos = text.index("insert linestart") + + text.undo_block_start() # Allow action to be undone as a single unit. + text.insert(pos, banner) + text.undo_block_stop() + + # Now set the insert point to the middle of the banner. + line, col = [int(s) for s in pos.split(".")] + text.mark_set("insert", "%d.1 lineend" % (line+2, ) ) + + +# Here is a sample event bound to the "Home" key in the +# interactive window +def InteractiveHome(editor_window, event): + return _DoInteractiveHome(editor_window.text, 0) + +def InteractiveHomeExtend(editor_window, event): + return _DoInteractiveHome(editor_window.text, 1) + +def _DoInteractiveHome(text, extend): + import sys + # If Scintilla has an autocomplete window open, then let Scintilla handle it. + if text.edit.SCIAutoCActive(): + return 1 + of_interest = "insert linestart + %d c" % len(sys.ps1) + if not text.compare("insert", "==", of_interest) and \ + text.get("insert linestart", of_interest) in [sys.ps1, sys.ps2]: # Not sys.ps? line + end = of_interest + else: + end = "insert linestart" + + if extend: start = "insert" + else: start = end + text.tag_add("sel", start, end) + +# From Niki Spahie +def AutoFindNext(editor_window, event): + "find selected text or word under cursor" + + from pywin.scintilla import find + from pywin.scintilla import scintillacon + + try: + sci = editor_window.edit + word = sci.GetSelText() + if word: + find.lastSearch.findText = word + find.lastSearch.sel = sci.GetSel() + else: + pos = sci.SendScintilla( scintillacon.SCI_GETCURRENTPOS ) + start = sci.SendScintilla( scintillacon.SCI_WORDSTARTPOSITION, pos, 1 ) + end = sci.SendScintilla( scintillacon.SCI_WORDENDPOSITION, pos, 1 ) + word = sci.GetTextRange( start, end ) + if word: + find.lastSearch.findText = word + find.lastSearch.sel = (start,end) + except Exception: + import traceback + traceback.print_exc() + find.FindNext() + + +# A couple of generic events. +def Beep(editor_window, event): + editor_window.text.beep() + +def DoNothing(editor_window, event): + pass + +def ContinueEvent(editor_window, event): + # Almost an "unbind" - allows Pythonwin/MFC to handle the keystroke + return 1 + diff --git a/myenv/Lib/site-packages/pythonwin/pywin/dialogs/__init__.py b/myenv/Lib/site-packages/pythonwin/pywin/dialogs/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/myenv/Lib/site-packages/pythonwin/pywin/dialogs/ideoptions.py b/myenv/Lib/site-packages/pythonwin/pywin/dialogs/ideoptions.py new file mode 100644 index 000000000..f1bae90df --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/dialogs/ideoptions.py @@ -0,0 +1,139 @@ +# The property page to define generic IDE options for Pythonwin + +import win32con +import win32ui +from pywin.framework import interact +from pywin.mfc import dialog + +buttonControlMap = { + win32ui.IDC_BUTTON1: win32ui.IDC_EDIT1, + win32ui.IDC_BUTTON2: win32ui.IDC_EDIT2, + win32ui.IDC_BUTTON3: win32ui.IDC_EDIT3, +} + + +class OptionsPropPage(dialog.PropertyPage): + def __init__(self): + dialog.PropertyPage.__init__(self, win32ui.IDD_PP_IDE) + self.AddDDX(win32ui.IDC_CHECK1, "bShowAtStartup") + self.AddDDX(win32ui.IDC_CHECK2, "bDocking") + self.AddDDX(win32ui.IDC_EDIT4, "MRUSize", "i") + + def OnInitDialog(self): + edit = self.GetDlgItem(win32ui.IDC_EDIT1) + format = eval( + win32ui.GetProfileVal( + interact.sectionProfile, + interact.STYLE_INTERACTIVE_PROMPT, + str(interact.formatInput), + ) + ) + edit.SetDefaultCharFormat(format) + edit.SetWindowText("Input Text") + + edit = self.GetDlgItem(win32ui.IDC_EDIT2) + format = eval( + win32ui.GetProfileVal( + interact.sectionProfile, + interact.STYLE_INTERACTIVE_OUTPUT, + str(interact.formatOutput), + ) + ) + edit.SetDefaultCharFormat(format) + edit.SetWindowText("Output Text") + + edit = self.GetDlgItem(win32ui.IDC_EDIT3) + format = eval( + win32ui.GetProfileVal( + interact.sectionProfile, + interact.STYLE_INTERACTIVE_ERROR, + str(interact.formatOutputError), + ) + ) + edit.SetDefaultCharFormat(format) + edit.SetWindowText("Error Text") + + self["bShowAtStartup"] = interact.LoadPreference("Show at startup", 1) + self["bDocking"] = interact.LoadPreference("Docking", 0) + self["MRUSize"] = win32ui.GetProfileVal("Settings", "Recent File List Size", 10) + + # Hook the button clicks. + self.HookCommand(self.HandleCharFormatChange, win32ui.IDC_BUTTON1) + self.HookCommand(self.HandleCharFormatChange, win32ui.IDC_BUTTON2) + self.HookCommand(self.HandleCharFormatChange, win32ui.IDC_BUTTON3) + + # Ensure the spin control remains in range. + spinner = self.GetDlgItem(win32ui.IDC_SPIN1) + spinner.SetRange(1, 16) + + return dialog.PropertyPage.OnInitDialog(self) + + # Called to save away the new format tuple for the specified item. + def HandleCharFormatChange(self, id, code): + if code == win32con.BN_CLICKED: + editId = buttonControlMap.get(id) + assert editId is not None, "Format button has no associated edit control" + editControl = self.GetDlgItem(editId) + existingFormat = editControl.GetDefaultCharFormat() + flags = win32con.CF_SCREENFONTS + d = win32ui.CreateFontDialog(existingFormat, flags, None, self) + if d.DoModal() == win32con.IDOK: + cf = d.GetCharFormat() + editControl.SetDefaultCharFormat(cf) + self.SetModified(1) + return 0 # We handled this fully! + + def OnOK(self): + # Handle the edit controls - get all the fonts, put them back into interact, then + # get interact to save its stuff! + controlAttrs = [ + (win32ui.IDC_EDIT1, interact.STYLE_INTERACTIVE_PROMPT), + (win32ui.IDC_EDIT2, interact.STYLE_INTERACTIVE_OUTPUT), + (win32ui.IDC_EDIT3, interact.STYLE_INTERACTIVE_ERROR), + ] + for id, key in controlAttrs: + control = self.GetDlgItem(id) + fmt = control.GetDefaultCharFormat() + win32ui.WriteProfileVal(interact.sectionProfile, key, str(fmt)) + + # Save the other interactive window options. + interact.SavePreference("Show at startup", self["bShowAtStartup"]) + interact.SavePreference("Docking", self["bDocking"]) + + # And the other options. + win32ui.WriteProfileVal("Settings", "Recent File List Size", self["MRUSize"]) + + return 1 + + def ChangeFormat(self, fmtAttribute, fmt): + dlg = win32ui.CreateFontDialog(fmt) + if dlg.DoModal() != win32con.IDOK: + return None + return dlg.GetCharFormat() + + def OnFormatTitle(self, command, code): + fmt = self.GetFormat(interact.formatTitle) + if fmt: + formatTitle = fmt + SaveFontPreferences() + + def OnFormatInput(self, command, code): + global formatInput + fmt = self.GetFormat(formatInput) + if fmt: + formatInput = fmt + SaveFontPreferences() + + def OnFormatOutput(self, command, code): + global formatOutput + fmt = self.GetFormat(formatOutput) + if fmt: + formatOutput = fmt + SaveFontPreferences() + + def OnFormatError(self, command, code): + global formatOutputError + fmt = self.GetFormat(formatOutputError) + if fmt: + formatOutputError = fmt + SaveFontPreferences() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/dialogs/list.py b/myenv/Lib/site-packages/pythonwin/pywin/dialogs/list.py new file mode 100644 index 000000000..b9934ce71 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/dialogs/list.py @@ -0,0 +1,143 @@ +import commctrl +import win32api +import win32con +import win32ui +from pywin.mfc import dialog + + +class ListDialog(dialog.Dialog): + def __init__(self, title, list): + dialog.Dialog.__init__(self, self._maketemplate(title)) + self.HookMessage(self.on_size, win32con.WM_SIZE) + self.HookNotify(self.OnListItemChange, commctrl.LVN_ITEMCHANGED) + self.HookCommand(self.OnListClick, win32ui.IDC_LIST1) + self.items = list + + def _maketemplate(self, title): + style = win32con.WS_DLGFRAME | win32con.WS_SYSMENU | win32con.WS_VISIBLE + ls = ( + win32con.WS_CHILD + | win32con.WS_VISIBLE + | commctrl.LVS_ALIGNLEFT + | commctrl.LVS_REPORT + ) + bs = win32con.WS_CHILD | win32con.WS_VISIBLE + return [ + [title, (0, 0, 200, 200), style, None, (8, "MS Sans Serif")], + ["SysListView32", None, win32ui.IDC_LIST1, (0, 0, 200, 200), ls], + [128, "OK", win32con.IDOK, (10, 0, 50, 14), bs | win32con.BS_DEFPUSHBUTTON], + [128, "Cancel", win32con.IDCANCEL, (0, 0, 50, 14), bs], + ] + + def FillList(self): + size = self.GetWindowRect() + width = size[2] - size[0] - (10) + itemDetails = (commctrl.LVCFMT_LEFT, width, "Item", 0) + self.itemsControl.InsertColumn(0, itemDetails) + index = 0 + for item in self.items: + index = self.itemsControl.InsertItem(index + 1, str(item), 0) + + def OnListClick(self, id, code): + if code == commctrl.NM_DBLCLK: + self.EndDialog(win32con.IDOK) + return 1 + + def OnListItemChange(self, std, extra): + (hwndFrom, idFrom, code), ( + itemNotify, + sub, + newState, + oldState, + change, + point, + lparam, + ) = (std, extra) + oldSel = (oldState & commctrl.LVIS_SELECTED) != 0 + newSel = (newState & commctrl.LVIS_SELECTED) != 0 + if oldSel != newSel: + try: + self.selecteditem = itemNotify + self.butOK.EnableWindow(1) + except win32ui.error: + self.selecteditem = None + + def OnInitDialog(self): + rc = dialog.Dialog.OnInitDialog(self) + self.itemsControl = self.GetDlgItem(win32ui.IDC_LIST1) + self.butOK = self.GetDlgItem(win32con.IDOK) + self.butCancel = self.GetDlgItem(win32con.IDCANCEL) + + self.FillList() + + size = self.GetWindowRect() + self.LayoutControls(size[2] - size[0], size[3] - size[1]) + self.butOK.EnableWindow(0) # wait for first selection + return rc + + def LayoutControls(self, w, h): + self.itemsControl.MoveWindow((0, 0, w, h - 30)) + self.butCancel.MoveWindow((10, h - 24, 60, h - 4)) + self.butOK.MoveWindow((w - 60, h - 24, w - 10, h - 4)) + + def on_size(self, params): + lparam = params[3] + w = win32api.LOWORD(lparam) + h = win32api.HIWORD(lparam) + self.LayoutControls(w, h) + + +class ListsDialog(ListDialog): + def __init__(self, title, list, colHeadings=["Item"]): + ListDialog.__init__(self, title, list) + self.colHeadings = colHeadings + + def FillList(self): + index = 0 + size = self.GetWindowRect() + width = ( + size[2] - size[0] - (10) - win32api.GetSystemMetrics(win32con.SM_CXVSCROLL) + ) + numCols = len(self.colHeadings) + + for col in self.colHeadings: + itemDetails = (commctrl.LVCFMT_LEFT, int(width / numCols), col, 0) + self.itemsControl.InsertColumn(index, itemDetails) + index = index + 1 + index = 0 + for items in self.items: + index = self.itemsControl.InsertItem(index + 1, str(items[0]), 0) + for itemno in range(1, numCols): + item = items[itemno] + self.itemsControl.SetItemText(index, itemno, str(item)) + + +def SelectFromList(title, lst): + dlg = ListDialog(title, lst) + if dlg.DoModal() == win32con.IDOK: + return dlg.selecteditem + else: + return None + + +def SelectFromLists(title, lists, headings): + dlg = ListsDialog(title, lists, headings) + if dlg.DoModal() == win32con.IDOK: + return dlg.selecteditem + else: + return None + + +def test(): + # print SelectFromList('Single list', [1,2,3]) + print( + SelectFromLists( + "Multi-List", + [("1", 1, "a"), ("2", 2, "b"), ("3", 3, "c")], + ["Col 1", "Col 2"], + ) + ) + + +if __name__ == "__main__": + test() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/dialogs/login.py b/myenv/Lib/site-packages/pythonwin/pywin/dialogs/login.py new file mode 100644 index 000000000..2a3295693 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/dialogs/login.py @@ -0,0 +1,156 @@ +"""login -- PythonWin user ID and password dialog box + +(Adapted from originally distributed with Mark Hammond's PythonWin - +this now replaces it!) + +login.GetLogin() displays a modal "OK/Cancel" dialog box with input +fields for a user ID and password. The password field input is masked +with *'s. GetLogin takes two optional parameters, a window title, and a +default user ID. If these parameters are omitted, the title defaults to +"Login", and the user ID is left blank. GetLogin returns a (userid, password) +tuple. GetLogin can be called from scripts running on the console - i.e. you +don't need to write a full-blown GUI app to use it. + +login.GetPassword() is similar, except there is no username field. + +Example: +import pywin.dialogs.login +title = "FTP Login" +def_user = "fred" +userid, password = pywin.dialogs.login.GetLogin(title, def_user) + +Jim Eggleston, 28 August 1996 +Merged with dlgpass and moved to pywin.dialogs by Mark Hammond Jan 1998. +""" + +import win32api +import win32con +import win32ui +from pywin.mfc import dialog + + +def MakeLoginDlgTemplate(title): + style = ( + win32con.DS_MODALFRAME + | win32con.WS_POPUP + | win32con.WS_VISIBLE + | win32con.WS_CAPTION + | win32con.WS_SYSMENU + | win32con.DS_SETFONT + ) + cs = win32con.WS_CHILD | win32con.WS_VISIBLE + + # Window frame and title + dlg = [ + [title, (0, 0, 184, 40), style, None, (8, "MS Sans Serif")], + ] + + # ID label and text box + dlg.append([130, "User ID:", -1, (7, 9, 69, 9), cs | win32con.SS_LEFT]) + s = cs | win32con.WS_TABSTOP | win32con.WS_BORDER + dlg.append(["EDIT", None, win32ui.IDC_EDIT1, (50, 7, 60, 12), s]) + + # Password label and text box + dlg.append([130, "Password:", -1, (7, 22, 69, 9), cs | win32con.SS_LEFT]) + s = cs | win32con.WS_TABSTOP | win32con.WS_BORDER + dlg.append( + ["EDIT", None, win32ui.IDC_EDIT2, (50, 20, 60, 12), s | win32con.ES_PASSWORD] + ) + + # OK/Cancel Buttons + s = cs | win32con.WS_TABSTOP + dlg.append( + [128, "OK", win32con.IDOK, (124, 5, 50, 14), s | win32con.BS_DEFPUSHBUTTON] + ) + s = win32con.BS_PUSHBUTTON | s + dlg.append([128, "Cancel", win32con.IDCANCEL, (124, 20, 50, 14), s]) + return dlg + + +def MakePasswordDlgTemplate(title): + style = ( + win32con.DS_MODALFRAME + | win32con.WS_POPUP + | win32con.WS_VISIBLE + | win32con.WS_CAPTION + | win32con.WS_SYSMENU + | win32con.DS_SETFONT + ) + cs = win32con.WS_CHILD | win32con.WS_VISIBLE + # Window frame and title + dlg = [ + [title, (0, 0, 177, 45), style, None, (8, "MS Sans Serif")], + ] + + # Password label and text box + dlg.append([130, "Password:", -1, (7, 7, 69, 9), cs | win32con.SS_LEFT]) + s = cs | win32con.WS_TABSTOP | win32con.WS_BORDER + dlg.append( + ["EDIT", None, win32ui.IDC_EDIT1, (50, 7, 60, 12), s | win32con.ES_PASSWORD] + ) + + # OK/Cancel Buttons + s = cs | win32con.WS_TABSTOP | win32con.BS_PUSHBUTTON + dlg.append( + [128, "OK", win32con.IDOK, (124, 5, 50, 14), s | win32con.BS_DEFPUSHBUTTON] + ) + dlg.append([128, "Cancel", win32con.IDCANCEL, (124, 22, 50, 14), s]) + return dlg + + +class LoginDlg(dialog.Dialog): + Cancel = 0 + + def __init__(self, title): + dialog.Dialog.__init__(self, MakeLoginDlgTemplate(title)) + self.AddDDX(win32ui.IDC_EDIT1, "userid") + self.AddDDX(win32ui.IDC_EDIT2, "password") + + +def GetLogin(title="Login", userid="", password=""): + d = LoginDlg(title) + d["userid"] = userid + d["password"] = password + if d.DoModal() != win32con.IDOK: + return (None, None) + else: + return (d["userid"], d["password"]) + + +class PasswordDlg(dialog.Dialog): + def __init__(self, title): + dialog.Dialog.__init__(self, MakePasswordDlgTemplate(title)) + self.AddDDX(win32ui.IDC_EDIT1, "password") + + +def GetPassword(title="Password", password=""): + d = PasswordDlg(title) + d["password"] = password + if d.DoModal() != win32con.IDOK: + return None + return d["password"] + + +if __name__ == "__main__": + import sys + + title = "Login" + def_user = "" + if len(sys.argv) > 1: + title = sys.argv[1] + if len(sys.argv) > 2: + def_userid = sys.argv[2] + userid, password = GetLogin(title, def_user) + if userid == password == None: + print("User pressed Cancel") + else: + print("User ID: ", userid) + print("Password:", password) + newpassword = GetPassword("Reenter just for fun", password) + if newpassword is None: + print("User cancelled") + else: + what = "" + if newpassword != password: + what = "not " + print("The passwords did %smatch" % (what)) diff --git a/myenv/Lib/site-packages/pythonwin/pywin/dialogs/status.py b/myenv/Lib/site-packages/pythonwin/pywin/dialogs/status.py new file mode 100644 index 000000000..aef339148 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/dialogs/status.py @@ -0,0 +1,242 @@ +# No cancel button. + +import threading +import time + +import win32api +import win32con +import win32ui +from pywin.mfc import dialog +from pywin.mfc.thread import WinThread + + +def MakeProgressDlgTemplate(caption, staticText=""): + style = ( + win32con.DS_MODALFRAME + | win32con.WS_POPUP + | win32con.WS_VISIBLE + | win32con.WS_CAPTION + | win32con.WS_SYSMENU + | win32con.DS_SETFONT + ) + cs = win32con.WS_CHILD | win32con.WS_VISIBLE + + w = 215 + h = 36 # With button + h = 40 + + dlg = [ + [caption, (0, 0, w, h), style, None, (8, "MS Sans Serif")], + ] + + s = win32con.WS_TABSTOP | cs + + dlg.append([130, staticText, 1000, (7, 7, w - 7, h - 32), cs | win32con.SS_LEFT]) + + # dlg.append([128, + # "Cancel", + # win32con.IDCANCEL, + # (w - 60, h - 18, 50, 14), s | win32con.BS_PUSHBUTTON]) + + return dlg + + +class CStatusProgressDialog(dialog.Dialog): + def __init__(self, title, msg="", maxticks=100, tickincr=1): + self.initMsg = msg + templ = MakeProgressDlgTemplate(title, msg) + dialog.Dialog.__init__(self, templ) + self.maxticks = maxticks + self.tickincr = tickincr + self.pbar = None + + def OnInitDialog(self): + rc = dialog.Dialog.OnInitDialog(self) + self.static = self.GetDlgItem(1000) + self.pbar = win32ui.CreateProgressCtrl() + self.pbar.CreateWindow( + win32con.WS_CHILD | win32con.WS_VISIBLE, (10, 30, 310, 44), self, 1001 + ) + self.pbar.SetRange(0, self.maxticks) + self.pbar.SetStep(self.tickincr) + self.progress = 0 + self.pincr = 5 + return rc + + def Close(self): + self.EndDialog(0) + + def SetMaxTicks(self, maxticks): + if self.pbar is not None: + self.pbar.SetRange(0, maxticks) + + def Tick(self): + if self.pbar is not None: + self.pbar.StepIt() + + def SetTitle(self, text): + self.SetWindowText(text) + + def SetText(self, text): + self.SetDlgItemText(1000, text) + + def Set(self, pos, max=None): + if self.pbar is not None: + self.pbar.SetPos(pos) + if max is not None: + self.pbar.SetRange(0, max) + + +# a progress dialog created in a new thread - especially suitable for +# console apps with no message loop. +MYWM_SETTITLE = win32con.WM_USER + 10 +MYWM_SETMSG = win32con.WM_USER + 11 +MYWM_TICK = win32con.WM_USER + 12 +MYWM_SETMAXTICKS = win32con.WM_USER + 13 +MYWM_SET = win32con.WM_USER + 14 + + +class CThreadedStatusProcessDialog(CStatusProgressDialog): + def __init__(self, title, msg="", maxticks=100, tickincr=1): + self.title = title + self.msg = msg + self.threadid = win32api.GetCurrentThreadId() + CStatusProgressDialog.__init__(self, title, msg, maxticks, tickincr) + + def OnInitDialog(self): + rc = CStatusProgressDialog.OnInitDialog(self) + self.HookMessage(self.OnTitle, MYWM_SETTITLE) + self.HookMessage(self.OnMsg, MYWM_SETMSG) + self.HookMessage(self.OnTick, MYWM_TICK) + self.HookMessage(self.OnMaxTicks, MYWM_SETMAXTICKS) + self.HookMessage(self.OnSet, MYWM_SET) + return rc + + def _Send(self, msg): + try: + self.PostMessage(msg) + except win32ui.error: + # the user closed the window - but this does not cancel the + # process - so just ignore it. + pass + + def OnTitle(self, msg): + CStatusProgressDialog.SetTitle(self, self.title) + + def OnMsg(self, msg): + CStatusProgressDialog.SetText(self, self.msg) + + def OnTick(self, msg): + CStatusProgressDialog.Tick(self) + + def OnMaxTicks(self, msg): + CStatusProgressDialog.SetMaxTicks(self, self.maxticks) + + def OnSet(self, msg): + CStatusProgressDialog.Set(self, self.pos, self.max) + + def Close(self): + assert self.threadid, "No thread!" + win32api.PostThreadMessage(self.threadid, win32con.WM_QUIT, 0, 0) + + def SetMaxTicks(self, maxticks): + self.maxticks = maxticks + self._Send(MYWM_SETMAXTICKS) + + def SetTitle(self, title): + self.title = title + self._Send(MYWM_SETTITLE) + + def SetText(self, text): + self.msg = text + self._Send(MYWM_SETMSG) + + def Tick(self): + self._Send(MYWM_TICK) + + def Set(self, pos, max=None): + self.pos = pos + self.max = max + self._Send(MYWM_SET) + + +class ProgressThread(WinThread): + def __init__(self, title, msg="", maxticks=100, tickincr=1): + self.title = title + self.msg = msg + self.maxticks = maxticks + self.tickincr = tickincr + self.dialog = None + WinThread.__init__(self) + self.createdEvent = threading.Event() + + def InitInstance(self): + self.dialog = CThreadedStatusProcessDialog( + self.title, self.msg, self.maxticks, self.tickincr + ) + self.dialog.CreateWindow() + try: + self.dialog.SetForegroundWindow() + except win32ui.error: + pass + self.createdEvent.set() + return WinThread.InitInstance(self) + + def ExitInstance(self): + return 0 + + +def StatusProgressDialog(title, msg="", maxticks=100, parent=None): + d = CStatusProgressDialog(title, msg, maxticks) + d.CreateWindow(parent) + return d + + +def ThreadedStatusProgressDialog(title, msg="", maxticks=100): + t = ProgressThread(title, msg, maxticks) + t.CreateThread() + # Need to run a basic "PumpWaitingMessages" loop just incase we are + # running inside Pythonwin. + # Basic timeout incase things go terribly wrong. Ideally we should use + # win32event.MsgWaitForMultipleObjects(), but we use a threading module + # event - so use a dumb strategy + end_time = time.time() + 10 + while time.time() < end_time: + if t.createdEvent.isSet(): + break + win32ui.PumpWaitingMessages() + time.sleep(0.1) + return t.dialog + + +def demo(): + d = StatusProgressDialog("A Demo", "Doing something...") + import win32api + + for i in range(100): + if i == 50: + d.SetText("Getting there...") + if i == 90: + d.SetText("Nearly done...") + win32api.Sleep(20) + d.Tick() + d.Close() + + +def thread_demo(): + d = ThreadedStatusProgressDialog("A threaded demo", "Doing something") + import win32api + + for i in range(100): + if i == 50: + d.SetText("Getting there...") + if i == 90: + d.SetText("Nearly done...") + win32api.Sleep(20) + d.Tick() + d.Close() + + +if __name__ == "__main__": + thread_demo() + # demo() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/docking/DockingBar.py b/myenv/Lib/site-packages/pythonwin/pywin/docking/DockingBar.py new file mode 100644 index 000000000..3e51eb703 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/docking/DockingBar.py @@ -0,0 +1,679 @@ +# DockingBar.py + +# Ported directly (comments and all) from the samples at www.codeguru.com + +# WARNING: Use at your own risk, as this interface is highly likely to change. +# Currently we support only one child per DockingBar. Later we need to add +# support for multiple children. + +import struct + +import win32api +import win32con +import win32ui +from pywin.mfc import afxres, window + +clrBtnHilight = win32api.GetSysColor(win32con.COLOR_BTNHILIGHT) +clrBtnShadow = win32api.GetSysColor(win32con.COLOR_BTNSHADOW) + + +def CenterPoint(rect): + width = rect[2] - rect[0] + height = rect[3] - rect[1] + return rect[0] + width // 2, rect[1] + height // 2 + + +def OffsetRect(rect, point): + (x, y) = point + return rect[0] + x, rect[1] + y, rect[2] + x, rect[3] + y + + +def DeflateRect(rect, point): + (x, y) = point + return rect[0] + x, rect[1] + y, rect[2] - x, rect[3] - y + + +def PtInRect(rect, pt): + return rect[0] <= pt[0] < rect[2] and rect[1] <= pt[1] < rect[3] + + +class DockingBar(window.Wnd): + def __init__(self, obj=None): + if obj is None: + obj = win32ui.CreateControlBar() + window.Wnd.__init__(self, obj) + self.dialog = None + self.nDockBarID = 0 + self.sizeMin = 32, 32 + self.sizeHorz = 200, 200 + self.sizeVert = 200, 200 + self.sizeFloat = 200, 200 + self.bTracking = 0 + self.bInRecalcNC = 0 + self.cxEdge = 6 + self.cxBorder = 3 + self.cxGripper = 20 + self.brushBkgd = win32ui.CreateBrush() + self.brushBkgd.CreateSolidBrush(win32api.GetSysColor(win32con.COLOR_BTNFACE)) + + # Support for diagonal resizing + self.cyBorder = 3 + self.cCaptionSize = win32api.GetSystemMetrics(win32con.SM_CYSMCAPTION) + self.cMinWidth = win32api.GetSystemMetrics(win32con.SM_CXMIN) + self.cMinHeight = win32api.GetSystemMetrics(win32con.SM_CYMIN) + self.rectUndock = (0, 0, 0, 0) + + def OnUpdateCmdUI(self, target, bDisableIfNoHndler): + return self.UpdateDialogControls(target, bDisableIfNoHndler) + + def CreateWindow( + self, + parent, + childCreator, + title, + id, + style=win32con.WS_CHILD | win32con.WS_VISIBLE | afxres.CBRS_LEFT, + childCreatorArgs=(), + ): + assert not ( + (style & afxres.CBRS_SIZE_FIXED) and (style & afxres.CBRS_SIZE_DYNAMIC) + ), "Invalid style" + self.rectClose = self.rectBorder = self.rectGripper = self.rectTracker = ( + 0, + 0, + 0, + 0, + ) + + # save the style + self._obj_.dwStyle = style & afxres.CBRS_ALL + + cursor = win32api.LoadCursor(0, win32con.IDC_ARROW) + wndClass = win32ui.RegisterWndClass( + win32con.CS_DBLCLKS, cursor, self.brushBkgd.GetSafeHandle(), 0 + ) + + self._obj_.CreateWindow(wndClass, title, style, (0, 0, 0, 0), parent, id) + + # Create the child dialog + self.dialog = childCreator(*(self,) + childCreatorArgs) + + # use the dialog dimensions as default base dimensions + assert self.dialog.IsWindow(), ( + "The childCreator function %s did not create a window!" % childCreator + ) + rect = self.dialog.GetWindowRect() + self.sizeHorz = self.sizeVert = self.sizeFloat = ( + rect[2] - rect[0], + rect[3] - rect[1], + ) + + self.sizeHorz = self.sizeHorz[0], self.sizeHorz[1] + self.cxEdge + self.cxBorder + self.sizeVert = self.sizeVert[0] + self.cxEdge + self.cxBorder, self.sizeVert[1] + self.HookMessages() + + def CalcFixedLayout(self, bStretch, bHorz): + rectTop = self.dockSite.GetControlBar( + afxres.AFX_IDW_DOCKBAR_TOP + ).GetWindowRect() + rectLeft = self.dockSite.GetControlBar( + afxres.AFX_IDW_DOCKBAR_LEFT + ).GetWindowRect() + if bStretch: + nHorzDockBarWidth = 32767 + nVertDockBarHeight = 32767 + else: + nHorzDockBarWidth = rectTop[2] - rectTop[0] + 4 + nVertDockBarHeight = rectLeft[3] - rectLeft[1] + 4 + + if self.IsFloating(): + return self.sizeFloat + if bHorz: + return nHorzDockBarWidth, self.sizeHorz[1] + return self.sizeVert[0], nVertDockBarHeight + + def CalcDynamicLayout(self, length, mode): + # Support for diagonal sizing. + if self.IsFloating(): + self.GetParent().GetParent().ModifyStyle(win32ui.MFS_4THICKFRAME, 0) + if mode & (win32ui.LM_HORZDOCK | win32ui.LM_VERTDOCK): + flags = ( + win32con.SWP_NOSIZE + | win32con.SWP_NOMOVE + | win32con.SWP_NOZORDER + | win32con.SWP_NOACTIVATE + | win32con.SWP_FRAMECHANGED + ) + self.SetWindowPos( + 0, + ( + 0, + 0, + 0, + 0, + ), + flags, + ) + self.dockSite.RecalcLayout() + return self._obj_.CalcDynamicLayout(length, mode) + + if mode & win32ui.LM_MRUWIDTH: + return self.sizeFloat + if mode & win32ui.LM_COMMIT: + self.sizeFloat = length, self.sizeFloat[1] + return self.sizeFloat + # More diagonal sizing. + if self.IsFloating(): + dc = self.dockContext + pt = win32api.GetCursorPos() + windowRect = self.GetParent().GetParent().GetWindowRect() + + hittest = dc.nHitTest + if hittest == win32con.HTTOPLEFT: + cx = max(windowRect[2] - pt[0], self.cMinWidth) - self.cxBorder + cy = max(windowRect[3] - self.cCaptionSize - pt[1], self.cMinHeight) - 1 + self.sizeFloat = cx, cy + + top = ( + min(pt[1], windowRect[3] - self.cCaptionSize - self.cMinHeight) + - self.cyBorder + ) + left = min(pt[0], windowRect[2] - self.cMinWidth) - 1 + dc.rectFrameDragHorz = ( + left, + top, + dc.rectFrameDragHorz[2], + dc.rectFrameDragHorz[3], + ) + return self.sizeFloat + if hittest == win32con.HTTOPRIGHT: + cx = max(pt[0] - windowRect[0], self.cMinWidth) + cy = max(windowRect[3] - self.cCaptionSize - pt[1], self.cMinHeight) - 1 + self.sizeFloat = cx, cy + + top = ( + min(pt[1], windowRect[3] - self.cCaptionSize - self.cMinHeight) + - self.cyBorder + ) + dc.rectFrameDragHorz = ( + dc.rectFrameDragHorz[0], + top, + dc.rectFrameDragHorz[2], + dc.rectFrameDragHorz[3], + ) + return self.sizeFloat + + if hittest == win32con.HTBOTTOMLEFT: + cx = max(windowRect[2] - pt[0], self.cMinWidth) - self.cxBorder + cy = max(pt[1] - windowRect[1] - self.cCaptionSize, self.cMinHeight) + self.sizeFloat = cx, cy + + left = min(pt[0], windowRect[2] - self.cMinWidth) - 1 + dc.rectFrameDragHorz = ( + left, + dc.rectFrameDragHorz[1], + dc.rectFrameDragHorz[2], + dc.rectFrameDragHorz[3], + ) + return self.sizeFloat + + if hittest == win32con.HTBOTTOMRIGHT: + cx = max(pt[0] - windowRect[0], self.cMinWidth) + cy = max(pt[1] - windowRect[1] - self.cCaptionSize, self.cMinHeight) + self.sizeFloat = cx, cy + return self.sizeFloat + + if mode & win32ui.LM_LENGTHY: + self.sizeFloat = self.sizeFloat[0], max(self.sizeMin[1], length) + return self.sizeFloat + else: + return max(self.sizeMin[0], length), self.sizeFloat[1] + + def OnWindowPosChanged(self, msg): + if self.GetSafeHwnd() == 0 or self.dialog is None: + return 0 + lparam = msg[3] + """ LPARAM used with WM_WINDOWPOSCHANGED: + typedef struct { + HWND hwnd; + HWND hwndInsertAfter; + int x; + int y; + int cx; + int cy; + UINT flags;} WINDOWPOS; + """ + format = "PPiiiii" + bytes = win32ui.GetBytes(lparam, struct.calcsize(format)) + hwnd, hwndAfter, x, y, cx, cy, flags = struct.unpack(format, bytes) + + if self.bInRecalcNC: + rc = self.GetClientRect() + self.dialog.MoveWindow(rc) + return 0 + # Find on which side are we docked + nDockBarID = self.GetParent().GetDlgCtrlID() + # Return if dropped at same location + # no docking side change and no size change + if ( + (nDockBarID == self.nDockBarID) + and (flags & win32con.SWP_NOSIZE) + and ( + (self._obj_.dwStyle & afxres.CBRS_BORDER_ANY) != afxres.CBRS_BORDER_ANY + ) + ): + return + self.nDockBarID = nDockBarID + + # Force recalc the non-client area + self.bInRecalcNC = 1 + try: + swpflags = ( + win32con.SWP_NOSIZE + | win32con.SWP_NOMOVE + | win32con.SWP_NOZORDER + | win32con.SWP_FRAMECHANGED + ) + self.SetWindowPos(0, (0, 0, 0, 0), swpflags) + finally: + self.bInRecalcNC = 0 + return 0 + + # This is a virtual and not a message hook. + def OnSetCursor(self, window, nHitTest, wMouseMsg): + if nHitTest != win32con.HTSIZE or self.bTracking: + return self._obj_.OnSetCursor(window, nHitTest, wMouseMsg) + + if self.IsHorz(): + win32api.SetCursor(win32api.LoadCursor(0, win32con.IDC_SIZENS)) + else: + win32api.SetCursor(win32api.LoadCursor(0, win32con.IDC_SIZEWE)) + return 1 + + # Mouse Handling + def OnLButtonUp(self, msg): + if not self.bTracking: + return 1 # pass it on. + self.StopTracking(1) + return 0 # Dont pass on + + def OnLButtonDown(self, msg): + # UINT nFlags, CPoint point) + # only start dragging if clicked in "void" space + if self.dockBar is not None: + # start the drag + pt = msg[5] + pt = self.ClientToScreen(pt) + self.dockContext.StartDrag(pt) + return 0 + return 1 + + def OnNcLButtonDown(self, msg): + if self.bTracking: + return 0 + nHitTest = wparam = msg[2] + pt = msg[5] + + if nHitTest == win32con.HTSYSMENU and not self.IsFloating(): + self.GetDockingFrame().ShowControlBar(self, 0, 0) + elif nHitTest == win32con.HTMINBUTTON and not self.IsFloating(): + self.dockContext.ToggleDocking() + elif ( + nHitTest == win32con.HTCAPTION + and not self.IsFloating() + and self.dockBar is not None + ): + self.dockContext.StartDrag(pt) + elif nHitTest == win32con.HTSIZE and not self.IsFloating(): + self.StartTracking() + else: + return 1 + return 0 + + def OnLButtonDblClk(self, msg): + # only toggle docking if clicked in "void" space + if self.dockBar is not None: + # toggle docking + self.dockContext.ToggleDocking() + return 0 + return 1 + + def OnNcLButtonDblClk(self, msg): + nHitTest = wparam = msg[2] + # UINT nHitTest, CPoint point) + if self.dockBar is not None and nHitTest == win32con.HTCAPTION: + # toggle docking + self.dockContext.ToggleDocking() + return 0 + return 1 + + def OnMouseMove(self, msg): + flags = wparam = msg[2] + lparam = msg[3] + if self.IsFloating() or not self.bTracking: + return 1 + + # Convert unsigned 16 bit to signed 32 bit. + x = win32api.LOWORD(lparam) + if x & 32768: + x = x | -65536 + y = win32api.HIWORD(lparam) + if y & 32768: + y = y | -65536 + pt = x, y + cpt = CenterPoint(self.rectTracker) + pt = self.ClientToWnd(pt) + if self.IsHorz(): + if cpt[1] != pt[1]: + self.OnInvertTracker(self.rectTracker) + self.rectTracker = OffsetRect(self.rectTracker, (0, pt[1] - cpt[1])) + self.OnInvertTracker(self.rectTracker) + else: + if cpt[0] != pt[0]: + self.OnInvertTracker(self.rectTracker) + self.rectTracker = OffsetRect(self.rectTracker, (pt[0] - cpt[0], 0)) + self.OnInvertTracker(self.rectTracker) + + return 0 # Dont pass it on. + + # def OnBarStyleChange(self, old, new): + + def OnNcCalcSize(self, bCalcValid, size_info): + (rc0, rc1, rc2, pos) = size_info + self.rectBorder = self.GetWindowRect() + self.rectBorder = OffsetRect( + self.rectBorder, (-self.rectBorder[0], -self.rectBorder[1]) + ) + + dwBorderStyle = self._obj_.dwStyle | afxres.CBRS_BORDER_ANY + + if self.nDockBarID == afxres.AFX_IDW_DOCKBAR_TOP: + dwBorderStyle = dwBorderStyle & ~afxres.CBRS_BORDER_BOTTOM + rc0.left = rc0.left + self.cxGripper + rc0.bottom = rc0.bottom - self.cxEdge + rc0.top = rc0.top + self.cxBorder + rc0.right = rc0.right - self.cxBorder + self.rectBorder = ( + self.rectBorder[0], + self.rectBorder[3] - self.cxEdge, + self.rectBorder[2], + self.rectBorder[3], + ) + elif self.nDockBarID == afxres.AFX_IDW_DOCKBAR_BOTTOM: + dwBorderStyle = dwBorderStyle & ~afxres.CBRS_BORDER_TOP + rc0.left = rc0.left + self.cxGripper + rc0.top = rc0.top + self.cxEdge + rc0.bottom = rc0.bottom - self.cxBorder + rc0.right = rc0.right - self.cxBorder + self.rectBorder = ( + self.rectBorder[0], + self.rectBorder[1], + self.rectBorder[2], + self.rectBorder[1] + self.cxEdge, + ) + elif self.nDockBarID == afxres.AFX_IDW_DOCKBAR_LEFT: + dwBorderStyle = dwBorderStyle & ~afxres.CBRS_BORDER_RIGHT + rc0.right = rc0.right - self.cxEdge + rc0.left = rc0.left + self.cxBorder + rc0.bottom = rc0.bottom - self.cxBorder + rc0.top = rc0.top + self.cxGripper + self.rectBorder = ( + self.rectBorder[2] - self.cxEdge, + self.rectBorder[1], + self.rectBorder[2], + self.rectBorder[3], + ) + elif self.nDockBarID == afxres.AFX_IDW_DOCKBAR_RIGHT: + dwBorderStyle = dwBorderStyle & ~afxres.CBRS_BORDER_LEFT + rc0.left = rc0.left + self.cxEdge + rc0.right = rc0.right - self.cxBorder + rc0.bottom = rc0.bottom - self.cxBorder + rc0.top = rc0.top + self.cxGripper + self.rectBorder = ( + self.rectBorder[0], + self.rectBorder[1], + self.rectBorder[0] + self.cxEdge, + self.rectBorder[3], + ) + else: + self.rectBorder = 0, 0, 0, 0 + + self.SetBarStyle(dwBorderStyle) + return 0 + + def OnNcPaint(self, msg): + self.EraseNonClient() + dc = self.GetWindowDC() + ctl = win32api.GetSysColor(win32con.COLOR_BTNHIGHLIGHT) + cbr = win32api.GetSysColor(win32con.COLOR_BTNSHADOW) + dc.Draw3dRect(self.rectBorder, ctl, cbr) + + self.DrawGripper(dc) + + rect = self.GetClientRect() + self.InvalidateRect(rect, 1) + return 0 + + def OnNcHitTest(self, pt): # A virtual, not a hooked message. + if self.IsFloating(): + return 1 + + ptOrig = pt + rect = self.GetWindowRect() + pt = pt[0] - rect[0], pt[1] - rect[1] + + if PtInRect(self.rectClose, pt): + return win32con.HTSYSMENU + elif PtInRect(self.rectUndock, pt): + return win32con.HTMINBUTTON + elif PtInRect(self.rectGripper, pt): + return win32con.HTCAPTION + elif PtInRect(self.rectBorder, pt): + return win32con.HTSIZE + else: + return self._obj_.OnNcHitTest(ptOrig) + + def StartTracking(self): + self.SetCapture() + + # make sure no updates are pending + self.RedrawWindow(None, None, win32con.RDW_ALLCHILDREN | win32con.RDW_UPDATENOW) + self.dockSite.LockWindowUpdate() + + self.ptOld = CenterPoint(self.rectBorder) + self.bTracking = 1 + + self.rectTracker = self.rectBorder + if not self.IsHorz(): + l, t, r, b = self.rectTracker + b = b - 4 + self.rectTracker = l, t, r, b + + self.OnInvertTracker(self.rectTracker) + + def OnCaptureChanged(self, msg): + hwnd = lparam = msg[3] + if self.bTracking and hwnd != self.GetSafeHwnd(): + self.StopTracking(0) # cancel tracking + return 1 + + def StopTracking(self, bAccept): + self.OnInvertTracker(self.rectTracker) + self.dockSite.UnlockWindowUpdate() + self.bTracking = 0 + self.ReleaseCapture() + if not bAccept: + return + + rcc = self.dockSite.GetWindowRect() + if self.IsHorz(): + newsize = self.sizeHorz[1] + maxsize = newsize + (rcc[3] - rcc[1]) + minsize = self.sizeMin[1] + else: + newsize = self.sizeVert[0] + maxsize = newsize + (rcc[2] - rcc[0]) + minsize = self.sizeMin[0] + + pt = CenterPoint(self.rectTracker) + if self.nDockBarID == afxres.AFX_IDW_DOCKBAR_TOP: + newsize = newsize + (pt[1] - self.ptOld[1]) + elif self.nDockBarID == afxres.AFX_IDW_DOCKBAR_BOTTOM: + newsize = newsize + (-pt[1] + self.ptOld[1]) + elif self.nDockBarID == afxres.AFX_IDW_DOCKBAR_LEFT: + newsize = newsize + (pt[0] - self.ptOld[0]) + elif self.nDockBarID == afxres.AFX_IDW_DOCKBAR_RIGHT: + newsize = newsize + (-pt[0] + self.ptOld[0]) + newsize = max(minsize, min(maxsize, newsize)) + if self.IsHorz(): + self.sizeHorz = self.sizeHorz[0], newsize + else: + self.sizeVert = newsize, self.sizeVert[1] + self.dockSite.RecalcLayout() + return 0 + + def OnInvertTracker(self, rect): + assert rect[2] - rect[0] > 0 and rect[3] - rect[1] > 0, "rect is empty" + assert self.bTracking + rcc = self.GetWindowRect() + rcf = self.dockSite.GetWindowRect() + + rect = OffsetRect(rect, (rcc[0] - rcf[0], rcc[1] - rcf[1])) + rect = DeflateRect(rect, (1, 1)) + + flags = win32con.DCX_WINDOW | win32con.DCX_CACHE | win32con.DCX_LOCKWINDOWUPDATE + dc = self.dockSite.GetDCEx(None, flags) + try: + brush = win32ui.GetHalftoneBrush() + oldBrush = dc.SelectObject(brush) + + dc.PatBlt( + (rect[0], rect[1]), + (rect[2] - rect[0], rect[3] - rect[1]), + win32con.PATINVERT, + ) + dc.SelectObject(oldBrush) + finally: + self.dockSite.ReleaseDC(dc) + + def IsHorz(self): + return ( + self.nDockBarID == afxres.AFX_IDW_DOCKBAR_TOP + or self.nDockBarID == afxres.AFX_IDW_DOCKBAR_BOTTOM + ) + + def ClientToWnd(self, pt): + x, y = pt + if self.nDockBarID == afxres.AFX_IDW_DOCKBAR_BOTTOM: + y = y + self.cxEdge + elif self.nDockBarID == afxres.AFX_IDW_DOCKBAR_RIGHT: + x = x + self.cxEdge + return x, y + + def DrawGripper(self, dc): + # no gripper if floating + if self._obj_.dwStyle & afxres.CBRS_FLOATING: + return + + # -==HACK==- + # in order to calculate the client area properly after docking, + # the client area must be recalculated twice (I have no idea why) + self.dockSite.RecalcLayout() + # -==END HACK==- + + gripper = self.GetWindowRect() + gripper = self.ScreenToClient(gripper) + gripper = OffsetRect(gripper, (-gripper[0], -gripper[1])) + gl, gt, gr, gb = gripper + + if self._obj_.dwStyle & afxres.CBRS_ORIENT_HORZ: + # gripper at left + self.rectGripper = gl, gt + 40, gl + 20, gb + # draw close box + self.rectClose = gl + 7, gt + 10, gl + 19, gt + 22 + dc.DrawFrameControl( + self.rectClose, win32con.DFC_CAPTION, win32con.DFCS_CAPTIONCLOSE + ) + # draw docking toggle box + self.rectUndock = OffsetRect(self.rectClose, (0, 13)) + dc.DrawFrameControl( + self.rectUndock, win32con.DFC_CAPTION, win32con.DFCS_CAPTIONMAX + ) + + gt = gt + 38 + gb = gb - 10 + gl = gl + 10 + gr = gl + 3 + gripper = gl, gt, gr, gb + dc.Draw3dRect(gripper, clrBtnHilight, clrBtnShadow) + dc.Draw3dRect(OffsetRect(gripper, (4, 0)), clrBtnHilight, clrBtnShadow) + else: + # gripper at top + self.rectGripper = gl, gt, gr - 40, gt + 20 + # draw close box + self.rectClose = gr - 21, gt + 7, gr - 10, gt + 18 + dc.DrawFrameControl( + self.rectClose, win32con.DFC_CAPTION, win32con.DFCS_CAPTIONCLOSE + ) + # draw docking toggle box + self.rectUndock = OffsetRect(self.rectClose, (-13, 0)) + dc.DrawFrameControl( + self.rectUndock, win32con.DFC_CAPTION, win32con.DFCS_CAPTIONMAX + ) + gr = gr - 38 + gl = gl + 10 + gt = gt + 10 + gb = gt + 3 + + gripper = gl, gt, gr, gb + dc.Draw3dRect(gripper, clrBtnHilight, clrBtnShadow) + dc.Draw3dRect(OffsetRect(gripper, (0, 4)), clrBtnHilight, clrBtnShadow) + + def HookMessages(self): + self.HookMessage(self.OnLButtonUp, win32con.WM_LBUTTONUP) + self.HookMessage(self.OnLButtonDown, win32con.WM_LBUTTONDOWN) + self.HookMessage(self.OnLButtonDblClk, win32con.WM_LBUTTONDBLCLK) + self.HookMessage(self.OnNcLButtonDown, win32con.WM_NCLBUTTONDOWN) + self.HookMessage(self.OnNcLButtonDblClk, win32con.WM_NCLBUTTONDBLCLK) + self.HookMessage(self.OnMouseMove, win32con.WM_MOUSEMOVE) + self.HookMessage(self.OnNcPaint, win32con.WM_NCPAINT) + self.HookMessage(self.OnCaptureChanged, win32con.WM_CAPTURECHANGED) + self.HookMessage(self.OnWindowPosChanged, win32con.WM_WINDOWPOSCHANGED) + + +# self.HookMessage(self.OnSize, win32con.WM_SIZE) + + +def EditCreator(parent): + d = win32ui.CreateEdit() + es = ( + win32con.WS_CHILD + | win32con.WS_VISIBLE + | win32con.WS_BORDER + | win32con.ES_MULTILINE + | win32con.ES_WANTRETURN + ) + d.CreateWindow(es, (0, 0, 150, 150), parent, 1000) + return d + + +def test(): + import pywin.mfc.dialog + + global bar + bar = DockingBar() + creator = EditCreator + bar.CreateWindow(win32ui.GetMainFrame(), creator, "Coolbar Demo", 0xFFFFF) + # win32ui.GetMainFrame().ShowControlBar(bar, 1, 0) + bar.SetBarStyle( + bar.GetBarStyle() + | afxres.CBRS_TOOLTIPS + | afxres.CBRS_FLYBY + | afxres.CBRS_SIZE_DYNAMIC + ) + bar.EnableDocking(afxres.CBRS_ALIGN_ANY) + win32ui.GetMainFrame().DockControlBar(bar, afxres.AFX_IDW_DOCKBAR_BOTTOM) + + +if __name__ == "__main__": + test() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/docking/__init__.py b/myenv/Lib/site-packages/pythonwin/pywin/docking/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/myenv/Lib/site-packages/pythonwin/pywin/framework/__init__.py b/myenv/Lib/site-packages/pythonwin/pywin/framework/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/myenv/Lib/site-packages/pythonwin/pywin/framework/app.py b/myenv/Lib/site-packages/pythonwin/pywin/framework/app.py new file mode 100644 index 000000000..f994f99f9 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/framework/app.py @@ -0,0 +1,457 @@ +# App.py +# Application stuff. +# The application is responsible for managing the main frame window. +# +# We also grab the FileOpen command, to invoke our Python editor +" The PythonWin application code. Manages most aspects of MDI, etc " +import os +import sys +import traceback + +import regutil +import win32api +import win32con +import win32ui +from pywin.mfc import afxres, dialog, window +from pywin.mfc.thread import WinApp + +from . import scriptutils + +## NOTE: App and AppBuild should NOT be used - instead, you should contruct your +## APP class manually whenever you like (just ensure you leave these 2 params None!) +## Whoever wants the generic "Application" should get it via win32iu.GetApp() + +# These are "legacy" +AppBuilder = None +App = None # default - if used, must end up a CApp derived class. + + +# Helpers that should one day be removed! +def AddIdleHandler(handler): + print( + "app.AddIdleHandler is deprecated - please use win32ui.GetApp().AddIdleHandler() instead." + ) + return win32ui.GetApp().AddIdleHandler(handler) + + +def DeleteIdleHandler(handler): + print( + "app.DeleteIdleHandler is deprecated - please use win32ui.GetApp().DeleteIdleHandler() instead." + ) + return win32ui.GetApp().DeleteIdleHandler(handler) + + +# Helper for writing a Window position by name, and later loading it. +def SaveWindowSize(section, rect, state=""): + """Writes a rectangle to an INI file + Args: section = section name in the applications INI file + rect = a rectangle in a (cy, cx, y, x) tuple + (same format as CREATESTRUCT position tuples).""" + left, top, right, bottom = rect + if state: + state = state + " " + win32ui.WriteProfileVal(section, state + "left", left) + win32ui.WriteProfileVal(section, state + "top", top) + win32ui.WriteProfileVal(section, state + "right", right) + win32ui.WriteProfileVal(section, state + "bottom", bottom) + + +def LoadWindowSize(section, state=""): + """Loads a section from an INI file, and returns a rect in a tuple (see SaveWindowSize)""" + if state: + state = state + " " + left = win32ui.GetProfileVal(section, state + "left", 0) + top = win32ui.GetProfileVal(section, state + "top", 0) + right = win32ui.GetProfileVal(section, state + "right", 0) + bottom = win32ui.GetProfileVal(section, state + "bottom", 0) + return (left, top, right, bottom) + + +def RectToCreateStructRect(rect): + return (rect[3] - rect[1], rect[2] - rect[0], rect[1], rect[0]) + + +# Define FrameWindow and Application objects +# +# The Main Frame of the application. +class MainFrame(window.MDIFrameWnd): + sectionPos = "Main Window" + statusBarIndicators = ( + afxres.ID_SEPARATOR, # // status line indicator + afxres.ID_INDICATOR_CAPS, + afxres.ID_INDICATOR_NUM, + afxres.ID_INDICATOR_SCRL, + win32ui.ID_INDICATOR_LINENUM, + win32ui.ID_INDICATOR_COLNUM, + ) + + def OnCreate(self, cs): + self._CreateStatusBar() + return 0 + + def _CreateStatusBar(self): + self.statusBar = win32ui.CreateStatusBar(self) + self.statusBar.SetIndicators(self.statusBarIndicators) + self.HookCommandUpdate(self.OnUpdatePosIndicator, win32ui.ID_INDICATOR_LINENUM) + self.HookCommandUpdate(self.OnUpdatePosIndicator, win32ui.ID_INDICATOR_COLNUM) + + def OnUpdatePosIndicator(self, cmdui): + editControl = scriptutils.GetActiveEditControl() + value = " " * 5 + if editControl is not None: + try: + startChar, endChar = editControl.GetSel() + lineNo = editControl.LineFromChar(startChar) + colNo = endChar - editControl.LineIndex(lineNo) + + if cmdui.m_nID == win32ui.ID_INDICATOR_LINENUM: + value = "%0*d" % (5, lineNo + 1) + else: + value = "%0*d" % (3, colNo + 1) + except win32ui.error: + pass + cmdui.SetText(value) + cmdui.Enable() + + def PreCreateWindow(self, cc): + cc = self._obj_.PreCreateWindow(cc) + pos = LoadWindowSize(self.sectionPos) + self.startRect = pos + if pos[2] - pos[0]: + rect = RectToCreateStructRect(pos) + cc = cc[0], cc[1], cc[2], cc[3], rect, cc[5], cc[6], cc[7], cc[8] + return cc + + def OnDestroy(self, msg): + # use GetWindowPlacement(), as it works even when min'd or max'd + rectNow = self.GetWindowPlacement()[4] + if rectNow != self.startRect: + SaveWindowSize(self.sectionPos, rectNow) + return 0 + + +class CApp(WinApp): + "A class for the application" + + def __init__(self): + self.oldCallbackCaller = None + WinApp.__init__(self, win32ui.GetApp()) + self.idleHandlers = [] + + def InitInstance(self): + "Called to crank up the app" + HookInput() + numMRU = win32ui.GetProfileVal("Settings", "Recent File List Size", 10) + win32ui.LoadStdProfileSettings(numMRU) + # self._obj_.InitMDIInstance() + if win32api.GetVersionEx()[0] < 4: + win32ui.SetDialogBkColor() + win32ui.Enable3dControls() + + # install a "callback caller" - a manager for the callbacks + # self.oldCallbackCaller = win32ui.InstallCallbackCaller(self.CallbackManager) + self.LoadMainFrame() + self.SetApplicationPaths() + + def ExitInstance(self): + "Called as the app dies - too late to prevent it here!" + win32ui.OutputDebug("Application shutdown\n") + # Restore the callback manager, if any. + try: + win32ui.InstallCallbackCaller(self.oldCallbackCaller) + except AttributeError: + pass + if self.oldCallbackCaller: + del self.oldCallbackCaller + self.frame = None # clean Python references to the now destroyed window object. + self.idleHandlers = [] + # Attempt cleanup if not already done! + if self._obj_: + self._obj_.AttachObject(None) + self._obj_ = None + global App + global AppBuilder + App = None + AppBuilder = None + return 0 + + def HaveIdleHandler(self, handler): + return handler in self.idleHandlers + + def AddIdleHandler(self, handler): + self.idleHandlers.append(handler) + + def DeleteIdleHandler(self, handler): + self.idleHandlers.remove(handler) + + def OnIdle(self, count): + try: + ret = 0 + handlers = self.idleHandlers[:] # copy list, as may be modified during loop + for handler in handlers: + try: + thisRet = handler(handler, count) + except: + print("Idle handler %s failed" % (repr(handler))) + traceback.print_exc() + print("Idle handler removed from list") + try: + self.DeleteIdleHandler(handler) + except ValueError: # Item not in list. + pass + thisRet = 0 + ret = ret or thisRet + return ret + except KeyboardInterrupt: + pass + + def CreateMainFrame(self): + return MainFrame() + + def LoadMainFrame(self): + "Create the main applications frame" + self.frame = self.CreateMainFrame() + self.SetMainFrame(self.frame) + self.frame.LoadFrame(win32ui.IDR_MAINFRAME, win32con.WS_OVERLAPPEDWINDOW) + self.frame.DragAcceptFiles() # we can accept these. + self.frame.ShowWindow(win32ui.GetInitialStateRequest()) + self.frame.UpdateWindow() + self.HookCommands() + + def OnHelp(self, id, code): + try: + if id == win32ui.ID_HELP_GUI_REF: + helpFile = regutil.GetRegisteredHelpFile("Pythonwin Reference") + helpCmd = win32con.HELP_CONTENTS + else: + helpFile = regutil.GetRegisteredHelpFile("Main Python Documentation") + helpCmd = win32con.HELP_FINDER + if helpFile is None: + win32ui.MessageBox("The help file is not registered!") + else: + from . import help + + help.OpenHelpFile(helpFile, helpCmd) + except: + t, v, tb = sys.exc_info() + win32ui.MessageBox( + "Internal error in help file processing\r\n%s: %s" % (t, v) + ) + tb = None # Prevent a cycle + + def DoLoadModules(self, modules): + # XXX - this should go, but the debugger uses it :-( + # dont do much checking! + for module in modules: + __import__(module) + + def HookCommands(self): + self.frame.HookMessage(self.OnDropFiles, win32con.WM_DROPFILES) + self.HookCommand(self.HandleOnFileOpen, win32ui.ID_FILE_OPEN) + self.HookCommand(self.HandleOnFileNew, win32ui.ID_FILE_NEW) + self.HookCommand(self.OnFileMRU, win32ui.ID_FILE_MRU_FILE1) + self.HookCommand(self.OnHelpAbout, win32ui.ID_APP_ABOUT) + self.HookCommand(self.OnHelp, win32ui.ID_HELP_PYTHON) + self.HookCommand(self.OnHelp, win32ui.ID_HELP_GUI_REF) + # Hook for the right-click menu. + self.frame.GetWindow(win32con.GW_CHILD).HookMessage( + self.OnRClick, win32con.WM_RBUTTONDOWN + ) + + def SetApplicationPaths(self): + # Load the users/application paths + new_path = [] + apppath = win32ui.GetProfileVal("Python", "Application Path", "").split(";") + for path in apppath: + if len(path) > 0: + new_path.append(win32ui.FullPath(path)) + for extra_num in range(1, 11): + apppath = win32ui.GetProfileVal( + "Python", "Application Path %d" % extra_num, "" + ).split(";") + if len(apppath) == 0: + break + for path in apppath: + if len(path) > 0: + new_path.append(win32ui.FullPath(path)) + sys.path = new_path + sys.path + + def OnRClick(self, params): + "Handle right click message" + # put up the entire FILE menu! + menu = win32ui.LoadMenu(win32ui.IDR_TEXTTYPE).GetSubMenu(0) + menu.TrackPopupMenu(params[5]) # track at mouse position. + return 0 + + def OnDropFiles(self, msg): + "Handle a file being dropped from file manager" + hDropInfo = msg[2] + self.frame.SetActiveWindow() # active us + nFiles = win32api.DragQueryFile(hDropInfo) + try: + for iFile in range(0, nFiles): + fileName = win32api.DragQueryFile(hDropInfo, iFile) + win32ui.GetApp().OpenDocumentFile(fileName) + finally: + win32api.DragFinish(hDropInfo) + + return 0 + + # No longer used by Pythonwin, as the C++ code has this same basic functionality + # but handles errors slightly better. + # It all still works, tho, so if you need similar functionality, you can use it. + # Therefore I havent deleted this code completely! + # def CallbackManager( self, ob, args = () ): + # """Manage win32 callbacks. Trap exceptions, report on them, then return 'All OK' + # to the frame-work. """ + # import traceback + # try: + # ret = apply(ob, args) + # return ret + # except: + # # take copies of the exception values, else other (handled) exceptions may get + # # copied over by the other fns called. + # win32ui.SetStatusText('An exception occured in a windows command handler.') + # t, v, tb = sys.exc_info() + # traceback.print_exception(t, v, tb.tb_next) + # try: + # sys.stdout.flush() + # except (NameError, AttributeError): + # pass + + # Command handlers. + def OnFileMRU(self, id, code): + "Called when a File 1-n message is recieved" + fileName = win32ui.GetRecentFileList()[id - win32ui.ID_FILE_MRU_FILE1] + win32ui.GetApp().OpenDocumentFile(fileName) + + def HandleOnFileOpen(self, id, code): + "Called when FileOpen message is received" + win32ui.GetApp().OnFileOpen() + + def HandleOnFileNew(self, id, code): + "Called when FileNew message is received" + win32ui.GetApp().OnFileNew() + + def OnHelpAbout(self, id, code): + "Called when HelpAbout message is received. Displays the About dialog." + win32ui.InitRichEdit() + dlg = AboutBox() + dlg.DoModal() + + +def _GetRegistryValue(key, val, default=None): + # val is registry value - None for default val. + try: + hkey = win32api.RegOpenKey(win32con.HKEY_CURRENT_USER, key) + return win32api.RegQueryValueEx(hkey, val)[0] + except win32api.error: + try: + hkey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, key) + return win32api.RegQueryValueEx(hkey, val)[0] + except win32api.error: + return default + + +scintilla = "Scintilla is Copyright 1998-2008 Neil Hodgson (http://www.scintilla.org)" +idle = "This program uses IDLE extensions by Guido van Rossum, Tim Peters and others." +contributors = "Thanks to the following people for making significant contributions: Roger Upole, Sidnei da Silva, Sam Rushing, Curt Hagenlocher, Dave Brennan, Roger Burnham, Gordon McMillan, Neil Hodgson, Laramie Leavitt. (let me know if I have forgotten you!)" + + +# The About Box +class AboutBox(dialog.Dialog): + def __init__(self, idd=win32ui.IDD_ABOUTBOX): + dialog.Dialog.__init__(self, idd) + + def OnInitDialog(self): + text = ( + "Pythonwin - Python IDE and GUI Framework for Windows.\n\n%s\n\nPython is %s\n\n%s\n\n%s\n\n%s" + % (win32ui.copyright, sys.copyright, scintilla, idle, contributors) + ) + self.SetDlgItemText(win32ui.IDC_EDIT1, text) + # Get the build number - written by installers. + # For distutils build, read pywin32.version.txt + import sysconfig + + site_packages = sysconfig.get_paths()["platlib"] + try: + build_no = ( + open(os.path.join(site_packages, "pywin32.version.txt")).read().strip() + ) + ver = "pywin32 build %s" % build_no + except EnvironmentError: + ver = None + if ver is None: + # See if we are Part of Active Python + ver = _GetRegistryValue( + "SOFTWARE\\ActiveState\\ActivePython", "CurrentVersion" + ) + if ver is not None: + ver = "ActivePython build %s" % (ver,) + if ver is None: + ver = "" + self.SetDlgItemText(win32ui.IDC_ABOUT_VERSION, ver) + self.HookCommand(self.OnButHomePage, win32ui.IDC_BUTTON1) + + def OnButHomePage(self, id, code): + if code == win32con.BN_CLICKED: + win32api.ShellExecute( + 0, "open", "https://github.com/mhammond/pywin32", None, "", 1 + ) + + +def Win32RawInput(prompt=None): + "Provide raw_input() for gui apps" + # flush stderr/out first. + try: + sys.stdout.flush() + sys.stderr.flush() + except: + pass + if prompt is None: + prompt = "" + ret = dialog.GetSimpleInput(prompt) + if ret == None: + raise KeyboardInterrupt("operation cancelled") + return ret + + +def Win32Input(prompt=None): + "Provide input() for gui apps" + return eval(input(prompt)) + + +def HookInput(): + try: + raw_input + # must be py2x... + sys.modules["__builtin__"].raw_input = Win32RawInput + sys.modules["__builtin__"].input = Win32Input + except NameError: + # must be py3k + import code + + sys.modules["builtins"].input = Win32RawInput + + +def HaveGoodGUI(): + """Returns true if we currently have a good gui available.""" + return "pywin.framework.startup" in sys.modules + + +def CreateDefaultGUI(appClass=None): + """Creates a default GUI environment""" + if appClass is None: + from . import intpyapp # Bring in the default app - could be param'd later. + + appClass = intpyapp.InteractivePythonApp + # Create and init the app. + appClass().InitInstance() + + +def CheckCreateDefaultGUI(): + """Checks and creates if necessary a default GUI environment.""" + rc = HaveGoodGUI() + if not rc: + CreateDefaultGUI() + return rc diff --git a/myenv/Lib/site-packages/pythonwin/pywin/framework/bitmap.py b/myenv/Lib/site-packages/pythonwin/pywin/framework/bitmap.py new file mode 100644 index 000000000..1501f72d8 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/framework/bitmap.py @@ -0,0 +1,164 @@ +import os + +import win32api +import win32con +import win32ui +from pywin.mfc import docview, window + +from . import app + +bStretch = 1 + + +class BitmapDocument(docview.Document): + "A bitmap document. Holds the bitmap data itself." + + def __init__(self, template): + docview.Document.__init__(self, template) + self.bitmap = None + + def OnNewDocument(self): + # I can not create new bitmaps. + win32ui.MessageBox("Bitmaps can not be created.") + + def OnOpenDocument(self, filename): + self.bitmap = win32ui.CreateBitmap() + # init data members + f = open(filename, "rb") + try: + try: + self.bitmap.LoadBitmapFile(f) + except IOError: + win32ui.MessageBox("Could not load the bitmap from %s" % filename) + return 0 + finally: + f.close() + self.size = self.bitmap.GetSize() + return 1 + + def DeleteContents(self): + self.bitmap = None + + +class BitmapView(docview.ScrollView): + "A view of a bitmap. Obtains data from document." + + def __init__(self, doc): + docview.ScrollView.__init__(self, doc) + self.width = self.height = 0 + # set up message handlers + self.HookMessage(self.OnSize, win32con.WM_SIZE) + + def OnInitialUpdate(self): + doc = self.GetDocument() + if doc.bitmap: + bitmapSize = doc.bitmap.GetSize() + self.SetScrollSizes(win32con.MM_TEXT, bitmapSize) + + def OnSize(self, params): + lParam = params[3] + self.width = win32api.LOWORD(lParam) + self.height = win32api.HIWORD(lParam) + + def OnDraw(self, dc): + # set sizes used for "non stretch" mode. + doc = self.GetDocument() + if doc.bitmap is None: + return + bitmapSize = doc.bitmap.GetSize() + if bStretch: + # stretch BMP. + viewRect = (0, 0, self.width, self.height) + bitmapRect = (0, 0, bitmapSize[0], bitmapSize[1]) + doc.bitmap.Paint(dc, viewRect, bitmapRect) + else: + # non stretch. + doc.bitmap.Paint(dc) + + +class BitmapFrame(window.MDIChildWnd): + def OnCreateClient(self, createparams, context): + borderX = win32api.GetSystemMetrics(win32con.SM_CXFRAME) + borderY = win32api.GetSystemMetrics(win32con.SM_CYFRAME) + titleY = win32api.GetSystemMetrics(win32con.SM_CYCAPTION) # includes border + # try and maintain default window pos, else adjust if cant fit + # get the main client window dimensions. + mdiClient = win32ui.GetMainFrame().GetWindow(win32con.GW_CHILD) + clientWindowRect = mdiClient.ScreenToClient(mdiClient.GetWindowRect()) + clientWindowSize = ( + clientWindowRect[2] - clientWindowRect[0], + clientWindowRect[3] - clientWindowRect[1], + ) + left, top, right, bottom = mdiClient.ScreenToClient(self.GetWindowRect()) + # width, height=context.doc.size[0], context.doc.size[1] + # width = width+borderX*2 + # height= height+titleY+borderY*2-1 + # if (left+width)>clientWindowSize[0]: + # left = clientWindowSize[0] - width + # if left<0: + # left = 0 + # width = clientWindowSize[0] + # if (top+height)>clientWindowSize[1]: + # top = clientWindowSize[1] - height + # if top<0: + # top = 0 + # height = clientWindowSize[1] + # self.frame.MoveWindow((left, top, left+width, top+height),0) + window.MDIChildWnd.OnCreateClient(self, createparams, context) + return 1 + + +class BitmapTemplate(docview.DocTemplate): + def __init__(self): + docview.DocTemplate.__init__( + self, win32ui.IDR_PYTHONTYPE, BitmapDocument, BitmapFrame, BitmapView + ) + + def MatchDocType(self, fileName, fileType): + doc = self.FindOpenDocument(fileName) + if doc: + return doc + ext = os.path.splitext(fileName)[1].lower() + if ext == ".bmp": # removed due to PIL! or ext=='.ppm': + return win32ui.CDocTemplate_Confidence_yesAttemptNative + return win32ui.CDocTemplate_Confidence_maybeAttemptForeign + + +# return win32ui.CDocTemplate_Confidence_noAttempt + +# For debugging purposes, when this module may be reloaded many times. +try: + win32ui.GetApp().RemoveDocTemplate(bitmapTemplate) +except NameError: + pass + +bitmapTemplate = BitmapTemplate() +bitmapTemplate.SetDocStrings( + "\nBitmap\nBitmap\nBitmap (*.bmp)\n.bmp\nPythonBitmapFileType\nPython Bitmap File" +) +win32ui.GetApp().AddDocTemplate(bitmapTemplate) + +# This works, but just didnt make it through the code reorg. +# class PPMBitmap(Bitmap): +# def LoadBitmapFile(self, file ): +# magic=file.readline() +# if magic <> "P6\n": +# raise TypeError, "The file is not a PPM format file" +# rowcollist=string.split(file.readline()) +# cols=string.atoi(rowcollist[0]) +# rows=string.atoi(rowcollist[1]) +# file.readline() # whats this one? +# self.bitmap.LoadPPMFile(file,(cols,rows)) + + +def t(): + bitmapTemplate.OpenDocumentFile("d:\\winnt\\arcade.bmp") + # OpenBMPFile( 'd:\\winnt\\arcade.bmp') + + +def demo(): + import glob + + winDir = win32api.GetWindowsDirectory() + for fileName in glob.glob1(winDir, "*.bmp")[:2]: + bitmapTemplate.OpenDocumentFile(os.path.join(winDir, fileName)) diff --git a/myenv/Lib/site-packages/pythonwin/pywin/framework/cmdline.py b/myenv/Lib/site-packages/pythonwin/pywin/framework/cmdline.py new file mode 100644 index 000000000..dba31d1ad --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/framework/cmdline.py @@ -0,0 +1,56 @@ +# cmdline - command line utilities. +import string +import sys + +import win32ui + + +def ParseArgs(str): + import string + + ret = [] + pos = 0 + length = len(str) + while pos < length: + try: + while str[pos] in string.whitespace: + pos = pos + 1 + except IndexError: + break + if pos >= length: + break + if str[pos] == '"': + pos = pos + 1 + try: + endPos = str.index('"', pos) - 1 + nextPos = endPos + 2 + except ValueError: + endPos = length + nextPos = endPos + 1 + else: + endPos = pos + while endPos < length and not str[endPos] in string.whitespace: + endPos = endPos + 1 + nextPos = endPos + 1 + ret.append(str[pos : endPos + 1].strip()) + pos = nextPos + return ret + + +def FixArgFileName(fileName): + """Convert a filename on the commandline to something useful. + Given an automatic filename on the commandline, turn it a python module name, + with the path added to sys.path.""" + import os + + path, fname = os.path.split(fileName) + if len(path) == 0: + path = os.curdir + path = os.path.abspath(path) + # must check that the command line arg's path is in sys.path + for syspath in sys.path: + if os.path.abspath(syspath) == path: + break + else: + sys.path.append(path) + return os.path.splitext(fname)[0] diff --git a/myenv/Lib/site-packages/pythonwin/pywin/framework/dbgcommands.py b/myenv/Lib/site-packages/pythonwin/pywin/framework/dbgcommands.py new file mode 100644 index 000000000..e295926c7 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/framework/dbgcommands.py @@ -0,0 +1,189 @@ +# Command Handlers for the debugger. + +# Not in the debugger package, as I always want these interfaces to be +# available, even if the debugger has not yet been (or can not be) +# imported +import warnings + +import win32ui +from pywin.scintilla.control import CScintillaEditInterface + +from . import scriptutils + +IdToBarNames = { + win32ui.IDC_DBG_STACK: ("Stack", 0), + win32ui.IDC_DBG_BREAKPOINTS: ("Breakpoints", 0), + win32ui.IDC_DBG_WATCH: ("Watch", 1), +} + + +class DebuggerCommandHandler: + def HookCommands(self): + commands = ( + (self.OnStep, None, win32ui.IDC_DBG_STEP), + (self.OnStepOut, self.OnUpdateOnlyBreak, win32ui.IDC_DBG_STEPOUT), + (self.OnStepOver, None, win32ui.IDC_DBG_STEPOVER), + (self.OnGo, None, win32ui.IDC_DBG_GO), + (self.OnClose, self.OnUpdateClose, win32ui.IDC_DBG_CLOSE), + (self.OnAdd, self.OnUpdateAddBreakpoints, win32ui.IDC_DBG_ADD), + (self.OnClearAll, self.OnUpdateClearAllBreakpoints, win32ui.IDC_DBG_CLEAR), + # (self.OnDebuggerToolbar, self.OnUpdateDebuggerToolbar, win32ui.ID_DEBUGGER_TOOLBAR), + ) + + frame = win32ui.GetMainFrame() + + for methHandler, methUpdate, id in commands: + frame.HookCommand(methHandler, id) + if not methUpdate is None: + frame.HookCommandUpdate(methUpdate, id) + + for id in list(IdToBarNames.keys()): + frame.HookCommand(self.OnDebuggerBar, id) + frame.HookCommandUpdate(self.OnUpdateDebuggerBar, id) + + def OnDebuggerToolbar(self, id, code): + if code == 0: + return not win32ui.GetMainFrame().OnBarCheck(id) + + def OnUpdateDebuggerToolbar(self, cmdui): + win32ui.GetMainFrame().OnUpdateControlBarMenu(cmdui) + cmdui.Enable(1) + + def _GetDebugger(self): + try: + import pywin.debugger + + return pywin.debugger.currentDebugger + except ImportError: + return None + + def _DoOrStart(self, doMethod, startFlag): + d = self._GetDebugger() + if d is not None and d.IsDebugging(): + method = getattr(d, doMethod) + method() + else: + scriptutils.RunScript( + defName=None, defArgs=None, bShowDialog=0, debuggingType=startFlag + ) + + def OnStep(self, msg, code): + self._DoOrStart("do_set_step", scriptutils.RS_DEBUGGER_STEP) + + def OnStepOver(self, msg, code): + self._DoOrStart("do_set_next", scriptutils.RS_DEBUGGER_STEP) + + def OnStepOut(self, msg, code): + d = self._GetDebugger() + if d is not None and d.IsDebugging(): + d.do_set_return() + + def OnGo(self, msg, code): + self._DoOrStart("do_set_continue", scriptutils.RS_DEBUGGER_GO) + + def OnClose(self, msg, code): + d = self._GetDebugger() + if d is not None: + if d.IsDebugging(): + d.set_quit() + else: + d.close() + + def OnUpdateClose(self, cmdui): + d = self._GetDebugger() + if d is not None and d.inited: + cmdui.Enable(1) + else: + cmdui.Enable(0) + + def OnAdd(self, msg, code): + doc, view = scriptutils.GetActiveEditorDocument() + if doc is None: + ## Don't do a messagebox, as this could be triggered from the app's + ## idle loop whenever the debug toolbar is visible, giving a never-ending + ## series of dialogs. This can happen when the OnUpdate handler + ## for the toolbar button IDC_DBG_ADD fails, since MFC falls back to + ## sending a normal command if the UI update command fails. + ## win32ui.MessageBox('There is no active window - no breakpoint can be added') + warnings.warn("There is no active window - no breakpoint can be added") + return None + pathName = doc.GetPathName() + lineNo = view.LineFromChar(view.GetSel()[0]) + 1 + # If I have a debugger, then tell it, otherwise just add a marker + d = self._GetDebugger() + if d is None: + import pywin.framework.editor.color.coloreditor + + doc.MarkerToggle( + lineNo, pywin.framework.editor.color.coloreditor.MARKER_BREAKPOINT + ) + else: + if d.get_break(pathName, lineNo): + win32ui.SetStatusText("Clearing breakpoint", 1) + rc = d.clear_break(pathName, lineNo) + else: + win32ui.SetStatusText("Setting breakpoint", 1) + rc = d.set_break(pathName, lineNo) + if rc: + win32ui.MessageBox(rc) + d.GUIRespondDebuggerData() + + def OnClearAll(self, msg, code): + win32ui.SetStatusText("Clearing all breakpoints") + d = self._GetDebugger() + if d is None: + import pywin.framework.editor + import pywin.framework.editor.color.coloreditor + + for doc in pywin.framework.editor.editorTemplate.GetDocumentList(): + doc.MarkerDeleteAll( + pywin.framework.editor.color.coloreditor.MARKER_BREAKPOINT + ) + else: + d.clear_all_breaks() + d.UpdateAllLineStates() + d.GUIRespondDebuggerData() + + def OnUpdateOnlyBreak(self, cmdui): + d = self._GetDebugger() + ok = d is not None and d.IsBreak() + cmdui.Enable(ok) + + def OnUpdateAddBreakpoints(self, cmdui): + doc, view = scriptutils.GetActiveEditorDocument() + if doc is None or not isinstance(view, CScintillaEditInterface): + enabled = 0 + else: + enabled = 1 + lineNo = view.LineFromChar(view.GetSel()[0]) + 1 + import pywin.framework.editor.color.coloreditor + + cmdui.SetCheck( + doc.MarkerAtLine( + lineNo, pywin.framework.editor.color.coloreditor.MARKER_BREAKPOINT + ) + != 0 + ) + cmdui.Enable(enabled) + + def OnUpdateClearAllBreakpoints(self, cmdui): + d = self._GetDebugger() + cmdui.Enable(d is None or len(d.breaks) != 0) + + def OnUpdateDebuggerBar(self, cmdui): + name, always = IdToBarNames.get(cmdui.m_nID) + enabled = always + d = self._GetDebugger() + if d is not None and d.IsDebugging() and name is not None: + enabled = 1 + bar = d.GetDebuggerBar(name) + cmdui.SetCheck(bar.IsWindowVisible()) + cmdui.Enable(enabled) + + def OnDebuggerBar(self, id, code): + name = IdToBarNames.get(id)[0] + d = self._GetDebugger() + if d is not None and name is not None: + bar = d.GetDebuggerBar(name) + newState = not bar.IsWindowVisible() + win32ui.GetMainFrame().ShowControlBar(bar, newState, 1) diff --git a/myenv/Lib/site-packages/pythonwin/pywin/framework/dlgappcore.py b/myenv/Lib/site-packages/pythonwin/pywin/framework/dlgappcore.py new file mode 100644 index 000000000..4b60cc1c0 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/framework/dlgappcore.py @@ -0,0 +1,75 @@ +# dlgappcore. +# +# base classes for dialog based apps. + + +import win32api +import win32con +import win32ui +from pywin.mfc import dialog + +from . import app + +error = "Dialog Application Error" + + +class AppDialog(dialog.Dialog): + "The dialog box for the application" + + def __init__(self, id, dll=None): + self.iconId = win32ui.IDR_MAINFRAME + dialog.Dialog.__init__(self, id, dll) + + def OnInitDialog(self): + return dialog.Dialog.OnInitDialog(self) + + # Provide support for a dlg app using an icon + def OnPaint(self): + if not self.IsIconic(): + return self._obj_.OnPaint() + self.DefWindowProc(win32con.WM_ICONERASEBKGND, dc.GetHandleOutput(), 0) + left, top, right, bottom = self.GetClientRect() + left = (right - win32api.GetSystemMetrics(win32con.SM_CXICON)) >> 1 + top = (bottom - win32api.GetSystemMetrics(win32con.SM_CYICON)) >> 1 + hIcon = win32ui.GetApp().LoadIcon(self.iconId) + self.GetDC().DrawIcon((left, top), hIcon) + + # Only needed to provide a minimized icon (and this seems + # less important under win95/NT4 + def OnEraseBkgnd(self, dc): + if self.IsIconic(): + return 1 + else: + return self._obj_.OnEraseBkgnd(dc) + + def OnQueryDragIcon(self): + return win32ui.GetApp().LoadIcon(self.iconId) + + def PreDoModal(self): + pass + + +class DialogApp(app.CApp): + "An application class, for an app with main dialog box" + + def InitInstance(self): + # win32ui.SetProfileFileName('dlgapp.ini') + win32ui.LoadStdProfileSettings() + win32ui.EnableControlContainer() + win32ui.Enable3dControls() + self.dlg = self.frame = self.CreateDialog() + + if self.frame is None: + raise error("No dialog was created by CreateDialog()") + return + + self._obj_.InitDlgInstance(self.dlg) + self.PreDoModal() + self.dlg.PreDoModal() + self.dlg.DoModal() + + def CreateDialog(self): + pass + + def PreDoModal(self): + pass diff --git a/myenv/Lib/site-packages/pythonwin/pywin/framework/editor/ModuleBrowser.py b/myenv/Lib/site-packages/pythonwin/pywin/framework/editor/ModuleBrowser.py new file mode 100644 index 000000000..7fd965327 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/framework/editor/ModuleBrowser.py @@ -0,0 +1,235 @@ +# ModuleBrowser.py - A view that provides a module browser for an editor document. +import pyclbr + +import afxres +import commctrl +import pywin.framework.scriptutils +import pywin.mfc.docview +import win32api +import win32con +import win32ui +from pywin.tools import browser, hierlist + + +class HierListCLBRModule(hierlist.HierListItem): + def __init__(self, modName, clbrdata): + self.modName = modName + self.clbrdata = clbrdata + + def GetText(self): + return self.modName + + def GetSubList(self): + ret = [] + for item in self.clbrdata.values(): + if ( + item.__class__ != pyclbr.Class + ): # ie, it is a pyclbr Function instance (only introduced post 1.5.2) + ret.append(HierListCLBRFunction(item)) + else: + ret.append(HierListCLBRClass(item)) + ret.sort() + return ret + + def IsExpandable(self): + return 1 + + +class HierListCLBRItem(hierlist.HierListItem): + def __init__(self, name, file, lineno, suffix=""): + self.name = str(name) + self.file = file + self.lineno = lineno + self.suffix = suffix + + def __lt__(self, other): + return self.name < other.name + + def __eq__(self, other): + return self.name == other.name + + def GetText(self): + return self.name + self.suffix + + def TakeDefaultAction(self): + if self.file: + pywin.framework.scriptutils.JumpToDocument( + self.file, self.lineno, bScrollToTop=1 + ) + else: + win32ui.SetStatusText("Can not locate the source code for this object.") + + def PerformItemSelected(self): + if self.file is None: + msg = "%s - source can not be located." % (self.name,) + else: + msg = "%s defined at line %d of %s" % (self.name, self.lineno, self.file) + win32ui.SetStatusText(msg) + + +class HierListCLBRClass(HierListCLBRItem): + def __init__(self, clbrclass, suffix=""): + try: + name = clbrclass.name + file = clbrclass.file + lineno = clbrclass.lineno + self.super = clbrclass.super + self.methods = clbrclass.methods + except AttributeError: + name = clbrclass + file = lineno = None + self.super = [] + self.methods = {} + HierListCLBRItem.__init__(self, name, file, lineno, suffix) + + def GetSubList(self): + r1 = [] + for c in self.super: + r1.append(HierListCLBRClass(c, " (Parent class)")) + r1.sort() + r2 = [] + for meth, lineno in self.methods.items(): + r2.append(HierListCLBRMethod(meth, self.file, lineno)) + r2.sort() + return r1 + r2 + + def IsExpandable(self): + return len(self.methods) + len(self.super) + + def GetBitmapColumn(self): + return 21 + + +class HierListCLBRFunction(HierListCLBRItem): + def __init__(self, clbrfunc, suffix=""): + name = clbrfunc.name + file = clbrfunc.file + lineno = clbrfunc.lineno + HierListCLBRItem.__init__(self, name, file, lineno, suffix) + + def GetBitmapColumn(self): + return 22 + + +class HierListCLBRMethod(HierListCLBRItem): + def GetBitmapColumn(self): + return 22 + + +class HierListCLBRErrorItem(hierlist.HierListItem): + def __init__(self, text): + self.text = text + + def GetText(self): + return self.text + + def GetSubList(self): + return [HierListCLBRErrorItem(self.text)] + + def IsExpandable(self): + return 0 + + +class HierListCLBRErrorRoot(HierListCLBRErrorItem): + def IsExpandable(self): + return 1 + + +class BrowserView(pywin.mfc.docview.TreeView): + def OnInitialUpdate(self): + self.list = None + rc = self._obj_.OnInitialUpdate() + self.HookMessage(self.OnSize, win32con.WM_SIZE) + self.bDirty = 0 + self.destroying = 0 + return rc + + def DestroyBrowser(self): + self.DestroyList() + + def OnActivateView(self, activate, av, dv): + # print "AV", self.bDirty, activate + if activate: + self.CheckRefreshList() + return self._obj_.OnActivateView(activate, av, dv) + + def _MakeRoot(self): + path = self.GetDocument().GetPathName() + if not path: + return HierListCLBRErrorRoot( + "Error: Can not browse a file until it is saved" + ) + else: + mod, path = pywin.framework.scriptutils.GetPackageModuleName(path) + if self.bDirty: + what = "Refreshing" + # Hack for pyclbr being too smart + try: + del pyclbr._modules[mod] + except (KeyError, AttributeError): + pass + else: + what = "Building" + win32ui.SetStatusText("%s class list - please wait..." % (what,), 1) + win32ui.DoWaitCursor(1) + try: + reader = pyclbr.readmodule_ex # new version post 1.5.2 + except AttributeError: + reader = pyclbr.readmodule + try: + data = reader(mod, [path]) + if data: + return HierListCLBRModule(mod, data) + else: + return HierListCLBRErrorRoot("No Python classes in module.") + + finally: + win32ui.DoWaitCursor(0) + win32ui.SetStatusText(win32ui.LoadString(afxres.AFX_IDS_IDLEMESSAGE)) + + def DestroyList(self): + self.destroying = 1 + list = getattr( + self, "list", None + ) # If the document was not successfully opened, we may not have a list. + self.list = None + if list is not None: + list.HierTerm() + self.destroying = 0 + + def CheckMadeList(self): + if self.list is not None or self.destroying: + return + self.rootitem = root = self._MakeRoot() + self.list = list = hierlist.HierListWithItems(root, win32ui.IDB_BROWSER_HIER) + list.HierInit(self.GetParentFrame(), self) + list.SetStyle( + commctrl.TVS_HASLINES | commctrl.TVS_LINESATROOT | commctrl.TVS_HASBUTTONS + ) + + def CheckRefreshList(self): + if self.bDirty: + if self.list is None: + self.CheckMadeList() + else: + new_root = self._MakeRoot() + if self.rootitem.__class__ == new_root.__class__ == HierListCLBRModule: + self.rootitem.modName = new_root.modName + self.rootitem.clbrdata = new_root.clbrdata + self.list.Refresh() + else: + self.list.AcceptRoot(self._MakeRoot()) + self.bDirty = 0 + + def OnSize(self, params): + lparam = params[3] + w = win32api.LOWORD(lparam) + h = win32api.HIWORD(lparam) + if w != 0: + self.CheckMadeList() + elif w == 0: + self.DestroyList() + return 1 + + def _UpdateUIForState(self): + self.bDirty = 1 diff --git a/myenv/Lib/site-packages/pythonwin/pywin/framework/editor/__init__.py b/myenv/Lib/site-packages/pythonwin/pywin/framework/editor/__init__.py new file mode 100644 index 000000000..ce13ece65 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/framework/editor/__init__.py @@ -0,0 +1,106 @@ +# __init__ for the Pythonwin editor package. +# +# We used to support optional editors - eg, color or non-color. +# +# This really isnt necessary with Scintilla, and scintilla +# is getting so deeply embedded that it was too much work. + +import sys + +import win32con +import win32ui + +defaultCharacterFormat = (-402653169, 0, 200, 0, 0, 0, 49, "Courier New") + +##def GetDefaultEditorModuleName(): +## import pywin +## # If someone has set pywin.editormodulename, then this is what we use +## try: +## prefModule = pywin.editormodulename +## except AttributeError: +## prefModule = win32ui.GetProfileVal("Editor","Module", "") +## return prefModule +## +##def WriteDefaultEditorModule(module): +## try: +## module = module.__name__ +## except: +## pass +## win32ui.WriteProfileVal("Editor", "Module", module) + + +def LoadDefaultEditor(): + pass + + +## prefModule = GetDefaultEditorModuleName() +## restorePrefModule = None +## mod = None +## if prefModule: +## try: +## mod = __import__(prefModule) +## except 'xx': +## msg = "Importing your preferred editor ('%s') failed.\n\nError %s: %s\n\nAn attempt will be made to load the default editor.\n\nWould you like this editor disabled in the future?" % (prefModule, sys.exc_info()[0], sys.exc_info()[1]) +## rc = win32ui.MessageBox(msg, "Error importing editor", win32con.MB_YESNO) +## if rc == win32con.IDNO: +## restorePrefModule = prefModule +## WriteDefaultEditorModule("") +## del rc +## +## try: +## # Try and load the default one - dont catch errors here. +## if mod is None: +## prefModule = "pywin.framework.editor.color.coloreditor" +## mod = __import__(prefModule) +## +## # Get at the real module. +## mod = sys.modules[prefModule] +## +## # Do a "from mod import *" +## globals().update(mod.__dict__) +## +## finally: +## # Restore the users default editor if it failed and they requested not to disable it. +## if restorePrefModule: +## WriteDefaultEditorModule(restorePrefModule) + + +def GetEditorOption(option, defaultValue, min=None, max=None): + rc = win32ui.GetProfileVal("Editor", option, defaultValue) + if min is not None and rc < min: + rc = defaultValue + if max is not None and rc > max: + rc = defaultValue + return rc + + +def SetEditorOption(option, newValue): + win32ui.WriteProfileVal("Editor", option, newValue) + + +def DeleteEditorOption(option): + try: + win32ui.WriteProfileVal("Editor", option, None) + except win32ui.error: + pass + + +# Load and save font tuples +def GetEditorFontOption(option, default=None): + if default is None: + default = defaultCharacterFormat + fmt = GetEditorOption(option, "") + if fmt == "": + return default + try: + return eval(fmt) + except: + print("WARNING: Invalid font setting in registry - setting ignored") + return default + + +def SetEditorFontOption(option, newValue): + SetEditorOption(option, str(newValue)) + + +from pywin.framework.editor.color.coloreditor import editorTemplate diff --git a/myenv/Lib/site-packages/pythonwin/pywin/framework/editor/color/__init__.py b/myenv/Lib/site-packages/pythonwin/pywin/framework/editor/color/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/myenv/Lib/site-packages/pythonwin/pywin/framework/editor/color/coloreditor.py b/myenv/Lib/site-packages/pythonwin/pywin/framework/editor/color/coloreditor.py new file mode 100644 index 000000000..4aacc0eab --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/framework/editor/color/coloreditor.py @@ -0,0 +1,664 @@ +# Color Editor originally by Neil Hodgson, but restructured by mh to integrate +# even tighter into Pythonwin. + +import pywin.scintilla.keycodes +import win32api +import win32con +import win32ui +from pywin.framework.editor import ( + GetEditorFontOption, + GetEditorOption, + SetEditorFontOption, + SetEditorOption, + defaultCharacterFormat, +) +from pywin.scintilla import bindings + +# from pywin.framework.editor import EditorPropertyPage + +MSG_CHECK_EXTERNAL_FILE = ( + win32con.WM_USER + 1999 +) ## WARNING: Duplicated in document.py and editor.py + +# Define a few common markers +MARKER_BOOKMARK = 0 +MARKER_BREAKPOINT = 1 +MARKER_CURRENT = 2 + +import pywin.scintilla.view +from pywin.debugger import dbgcon +from pywin.framework.editor.document import EditorDocumentBase +from pywin.scintilla import scintillacon # For the marker definitions +from pywin.scintilla.document import CScintillaDocument + + +class SyntEditDocument(EditorDocumentBase): + "A SyntEdit document." + + def OnDebuggerStateChange(self, state): + self._ApplyOptionalToViews("OnDebuggerStateChange", state) + + def HookViewNotifications(self, view): + EditorDocumentBase.HookViewNotifications(self, view) + view.SCISetUndoCollection(1) + + def FinalizeViewCreation(self, view): + EditorDocumentBase.FinalizeViewCreation(self, view) + if view == self.GetFirstView(): + self.GetDocTemplate().CheckIDLEMenus(view.idle) + + +SyntEditViewParent = pywin.scintilla.view.CScintillaView + + +class SyntEditView(SyntEditViewParent): + "A view of a SyntEdit. Obtains data from document." + + def __init__(self, doc): + SyntEditViewParent.__init__(self, doc) + self.bCheckingFile = 0 + + def OnInitialUpdate(self): + SyntEditViewParent.OnInitialUpdate(self) + + self.HookMessage(self.OnRClick, win32con.WM_RBUTTONDOWN) + + for id in ( + win32ui.ID_VIEW_FOLD_COLLAPSE, + win32ui.ID_VIEW_FOLD_COLLAPSE_ALL, + win32ui.ID_VIEW_FOLD_EXPAND, + win32ui.ID_VIEW_FOLD_EXPAND_ALL, + ): + self.HookCommand(self.OnCmdViewFold, id) + self.HookCommandUpdate(self.OnUpdateViewFold, id) + self.HookCommand(self.OnCmdViewFoldTopLevel, win32ui.ID_VIEW_FOLD_TOPLEVEL) + + # Define the markers + # self.SCIMarkerDeleteAll() + self.SCIMarkerDefineAll( + MARKER_BOOKMARK, + scintillacon.SC_MARK_ROUNDRECT, + win32api.RGB(0x0, 0x0, 0x0), + win32api.RGB(0, 0xFF, 0xFF), + ) + + self.SCIMarkerDefine(MARKER_CURRENT, scintillacon.SC_MARK_ARROW) + self.SCIMarkerSetBack(MARKER_CURRENT, win32api.RGB(0xFF, 0xFF, 0x00)) + + # Define the folding markers + if 1: # traditional markers + self.SCIMarkerDefineAll( + scintillacon.SC_MARKNUM_FOLDEROPEN, + scintillacon.SC_MARK_MINUS, + win32api.RGB(0xFF, 0xFF, 0xFF), + win32api.RGB(0, 0, 0), + ) + self.SCIMarkerDefineAll( + scintillacon.SC_MARKNUM_FOLDER, + scintillacon.SC_MARK_PLUS, + win32api.RGB(0xFF, 0xFF, 0xFF), + win32api.RGB(0, 0, 0), + ) + self.SCIMarkerDefineAll( + scintillacon.SC_MARKNUM_FOLDERSUB, + scintillacon.SC_MARK_EMPTY, + win32api.RGB(0xFF, 0xFF, 0xFF), + win32api.RGB(0, 0, 0), + ) + self.SCIMarkerDefineAll( + scintillacon.SC_MARKNUM_FOLDERTAIL, + scintillacon.SC_MARK_EMPTY, + win32api.RGB(0xFF, 0xFF, 0xFF), + win32api.RGB(0, 0, 0), + ) + self.SCIMarkerDefineAll( + scintillacon.SC_MARKNUM_FOLDEREND, + scintillacon.SC_MARK_EMPTY, + win32api.RGB(0xFF, 0xFF, 0xFF), + win32api.RGB(0, 0, 0), + ) + self.SCIMarkerDefineAll( + scintillacon.SC_MARKNUM_FOLDEROPENMID, + scintillacon.SC_MARK_EMPTY, + win32api.RGB(0xFF, 0xFF, 0xFF), + win32api.RGB(0, 0, 0), + ) + self.SCIMarkerDefineAll( + scintillacon.SC_MARKNUM_FOLDERMIDTAIL, + scintillacon.SC_MARK_EMPTY, + win32api.RGB(0xFF, 0xFF, 0xFF), + win32api.RGB(0, 0, 0), + ) + else: # curved markers + self.SCIMarkerDefineAll( + scintillacon.SC_MARKNUM_FOLDEROPEN, + scintillacon.SC_MARK_CIRCLEMINUS, + win32api.RGB(0xFF, 0xFF, 0xFF), + win32api.RGB(0, 0, 0), + ) + self.SCIMarkerDefineAll( + scintillacon.SC_MARKNUM_FOLDER, + scintillacon.SC_MARK_CIRCLEPLUS, + win32api.RGB(0xFF, 0xFF, 0xFF), + win32api.RGB(0, 0, 0), + ) + self.SCIMarkerDefineAll( + scintillacon.SC_MARKNUM_FOLDERSUB, + scintillacon.SC_MARK_VLINE, + win32api.RGB(0xFF, 0xFF, 0xFF), + win32api.RGB(0, 0, 0), + ) + self.SCIMarkerDefineAll( + scintillacon.SC_MARKNUM_FOLDERTAIL, + scintillacon.SC_MARK_LCORNERCURVE, + win32api.RGB(0xFF, 0xFF, 0xFF), + win32api.RGB(0, 0, 0), + ) + self.SCIMarkerDefineAll( + scintillacon.SC_MARKNUM_FOLDEREND, + scintillacon.SC_MARK_CIRCLEPLUSCONNECTED, + win32api.RGB(0xFF, 0xFF, 0xFF), + win32api.RGB(0, 0, 0), + ) + self.SCIMarkerDefineAll( + scintillacon.SC_MARKNUM_FOLDEROPENMID, + scintillacon.SC_MARK_CIRCLEMINUSCONNECTED, + win32api.RGB(0xFF, 0xFF, 0xFF), + win32api.RGB(0, 0, 0), + ) + self.SCIMarkerDefineAll( + scintillacon.SC_MARKNUM_FOLDERMIDTAIL, + scintillacon.SC_MARK_TCORNERCURVE, + win32api.RGB(0xFF, 0xFF, 0xFF), + win32api.RGB(0, 0, 0), + ) + + self.SCIMarkerDefine(MARKER_BREAKPOINT, scintillacon.SC_MARK_CIRCLE) + # Marker background depends on debugger state + self.SCIMarkerSetFore(MARKER_BREAKPOINT, win32api.RGB(0x0, 0, 0)) + # Get the current debugger state. + try: + import pywin.debugger + + if pywin.debugger.currentDebugger is None: + state = dbgcon.DBGSTATE_NOT_DEBUGGING + else: + state = pywin.debugger.currentDebugger.debuggerState + except ImportError: + state = dbgcon.DBGSTATE_NOT_DEBUGGING + self.OnDebuggerStateChange(state) + + def _GetSubConfigNames(self): + return ["editor"] # Allow [Keys:Editor] sections to be specific to us + + def DoConfigChange(self): + SyntEditViewParent.DoConfigChange(self) + tabSize = GetEditorOption("Tab Size", 4, 2) + indentSize = GetEditorOption("Indent Size", 4, 2) + bUseTabs = GetEditorOption("Use Tabs", 0) + bSmartTabs = GetEditorOption("Smart Tabs", 1) + ext = self.idle.IDLEExtension("AutoIndent") # Required extension. + + self.SCISetViewWS(GetEditorOption("View Whitespace", 0)) + self.SCISetViewEOL(GetEditorOption("View EOL", 0)) + self.SCISetIndentationGuides(GetEditorOption("View Indentation Guides", 0)) + + if GetEditorOption("Right Edge Enabled", 0): + mode = scintillacon.EDGE_BACKGROUND + else: + mode = scintillacon.EDGE_NONE + self.SCISetEdgeMode(mode) + self.SCISetEdgeColumn(GetEditorOption("Right Edge Column", 75)) + self.SCISetEdgeColor( + GetEditorOption("Right Edge Color", win32api.RGB(0xEF, 0xEF, 0xEF)) + ) + + width = GetEditorOption("Marker Margin Width", 16) + self.SCISetMarginWidthN(1, width) + width = GetEditorOption("Fold Margin Width", 12) + self.SCISetMarginWidthN(2, width) + width = GetEditorOption("Line Number Margin Width", 0) + self.SCISetMarginWidthN(0, width) + self.bFolding = GetEditorOption("Enable Folding", 1) + fold_flags = 0 + self.SendScintilla( + scintillacon.SCI_SETMODEVENTMASK, scintillacon.SC_MOD_CHANGEFOLD + ) + if self.bFolding: + if GetEditorOption("Fold Lines", 1): + fold_flags = 16 + + self.SCISetProperty("fold", self.bFolding) + self.SCISetFoldFlags(fold_flags) + + tt_color = GetEditorOption("Tab Timmy Color", win32api.RGB(0xFF, 0, 0)) + self.SendScintilla(scintillacon.SCI_INDICSETFORE, 1, tt_color) + + tt_use = GetEditorOption("Use Tab Timmy", 1) + if tt_use: + self.SCISetProperty("tab.timmy.whinge.level", "1") + + # Auto-indent has very complicated behaviour. In a nutshell, the only + # way to get sensible behaviour from it is to ensure tabwidth != indentsize. + # Further, usetabs will only ever go from 1->0, never 0->1. + # This is _not_ the behaviour Pythonwin wants: + # * Tab width is arbitary, so should have no impact on smarts. + # * bUseTabs setting should reflect how new files are created, and + # if Smart Tabs disabled, existing files are edited + # * If "Smart Tabs" is enabled, bUseTabs should have no bearing + # for existing files (unless of course no context can be determined) + # + # So for smart tabs we configure the widget with completely dummy + # values (ensuring tabwidth != indentwidth), ask it to guess, then + # look at the values it has guessed, and re-configure + if bSmartTabs: + ext.config(usetabs=1, tabwidth=5, indentwidth=4) + ext.set_indentation_params(1) + if ext.indentwidth == 5: + # Either 5 literal spaces, or a single tab character. Assume a tab + usetabs = 1 + indentwidth = tabSize + else: + # Either Indented with spaces, and indent size has been guessed or + # an empty file (or no context found - tough!) + if self.GetTextLength() == 0: # emtpy + usetabs = bUseTabs + indentwidth = indentSize + else: # guessed. + indentwidth = ext.indentwidth + usetabs = 0 + # Tab size can never be guessed - set at user preference. + ext.config(usetabs=usetabs, indentwidth=indentwidth, tabwidth=tabSize) + else: + # Dont want smart-tabs - just set the options! + ext.config(usetabs=bUseTabs, tabwidth=tabSize, indentwidth=indentSize) + self.SCISetIndent(indentSize) + self.SCISetTabWidth(tabSize) + + def OnDebuggerStateChange(self, state): + if state == dbgcon.DBGSTATE_NOT_DEBUGGING: + # Indicate breakpoints arent really usable. + # Not quite white - useful when no marker margin, so set as background color. + self.SCIMarkerSetBack(MARKER_BREAKPOINT, win32api.RGB(0xEF, 0xEF, 0xEF)) + else: + # A light-red, so still readable when no marker margin. + self.SCIMarkerSetBack(MARKER_BREAKPOINT, win32api.RGB(0xFF, 0x80, 0x80)) + + def HookDocumentHandlers(self): + SyntEditViewParent.HookDocumentHandlers(self) + self.HookMessage(self.OnCheckExternalDocumentUpdated, MSG_CHECK_EXTERNAL_FILE) + + def HookHandlers(self): + SyntEditViewParent.HookHandlers(self) + self.HookMessage(self.OnSetFocus, win32con.WM_SETFOCUS) + + def _PrepareUserStateChange(self): + return self.GetSel(), self.GetFirstVisibleLine() + + def _EndUserStateChange(self, info): + scrollOff = info[1] - self.GetFirstVisibleLine() + if scrollOff: + self.LineScroll(scrollOff) + # Make sure we dont reset the cursor beyond the buffer. + max = self.GetTextLength() + newPos = min(info[0][0], max), min(info[0][1], max) + self.SetSel(newPos) + + ####################################### + # The Windows Message or Notify handlers. + ####################################### + def OnMarginClick(self, std, extra): + notify = self.SCIUnpackNotifyMessage(extra) + if notify.margin == 2: # Our fold margin + line_click = self.LineFromChar(notify.position) + # max_line = self.GetLineCount() + if self.SCIGetFoldLevel(line_click) & scintillacon.SC_FOLDLEVELHEADERFLAG: + # If a fold point. + self.SCIToggleFold(line_click) + return 1 + + def OnSetFocus(self, msg): + # Even though we use file change notifications, we should be very sure about it here. + self.OnCheckExternalDocumentUpdated(msg) + return 1 + + def OnCheckExternalDocumentUpdated(self, msg): + if self.bCheckingFile: + return + self.bCheckingFile = 1 + self.GetDocument().CheckExternalDocumentUpdated() + self.bCheckingFile = 0 + + def OnRClick(self, params): + menu = win32ui.CreatePopupMenu() + self.AppendMenu(menu, "&Locate module", "LocateModule") + self.AppendMenu(menu, flags=win32con.MF_SEPARATOR) + self.AppendMenu(menu, "&Undo", "EditUndo") + self.AppendMenu(menu, "&Redo", "EditRedo") + self.AppendMenu(menu, flags=win32con.MF_SEPARATOR) + self.AppendMenu(menu, "Cu&t", "EditCut") + self.AppendMenu(menu, "&Copy", "EditCopy") + self.AppendMenu(menu, "&Paste", "EditPaste") + self.AppendMenu(menu, flags=win32con.MF_SEPARATOR) + self.AppendMenu(menu, "&Select all", "EditSelectAll") + self.AppendMenu( + menu, "View &Whitespace", "ViewWhitespace", checked=self.SCIGetViewWS() + ) + self.AppendMenu( + menu, "&Fixed Font", "ViewFixedFont", checked=self._GetColorizer().bUseFixed + ) + self.AppendMenu(menu, flags=win32con.MF_SEPARATOR) + self.AppendMenu(menu, "&Goto line...", "GotoLine") + + submenu = win32ui.CreatePopupMenu() + newitems = self.idle.GetMenuItems("edit") + for text, event in newitems: + self.AppendMenu(submenu, text, event) + + flags = win32con.MF_STRING | win32con.MF_ENABLED | win32con.MF_POPUP + menu.AppendMenu(flags, submenu.GetHandle(), "&Source code") + + flags = ( + win32con.TPM_LEFTALIGN | win32con.TPM_LEFTBUTTON | win32con.TPM_RIGHTBUTTON + ) + menu.TrackPopupMenu(params[5], flags, self) + return 0 + + def OnCmdViewFold(self, cid, code): # Handle the menu command + if cid == win32ui.ID_VIEW_FOLD_EXPAND_ALL: + self.FoldExpandAllEvent(None) + elif cid == win32ui.ID_VIEW_FOLD_EXPAND: + self.FoldExpandEvent(None) + elif cid == win32ui.ID_VIEW_FOLD_COLLAPSE_ALL: + self.FoldCollapseAllEvent(None) + elif cid == win32ui.ID_VIEW_FOLD_COLLAPSE: + self.FoldCollapseEvent(None) + else: + print("Unknown collapse/expand ID") + + def OnUpdateViewFold(self, cmdui): # Update the tick on the UI. + if not self.bFolding: + cmdui.Enable(0) + return + id = cmdui.m_nID + if id in (win32ui.ID_VIEW_FOLD_EXPAND_ALL, win32ui.ID_VIEW_FOLD_COLLAPSE_ALL): + cmdui.Enable() + else: + enable = 0 + lineno = self.LineFromChar(self.GetSel()[0]) + foldable = ( + self.SCIGetFoldLevel(lineno) & scintillacon.SC_FOLDLEVELHEADERFLAG + ) + is_expanded = self.SCIGetFoldExpanded(lineno) + if id == win32ui.ID_VIEW_FOLD_EXPAND: + if foldable and not is_expanded: + enable = 1 + elif id == win32ui.ID_VIEW_FOLD_COLLAPSE: + if foldable and is_expanded: + enable = 1 + cmdui.Enable(enable) + + def OnCmdViewFoldTopLevel(self, cid, code): # Handle the menu command + self.FoldTopLevelEvent(None) + + ####################################### + # The Events + ####################################### + def ToggleBookmarkEvent(self, event, pos=-1): + """Toggle a bookmark at the specified or current position""" + if pos == -1: + pos, end = self.GetSel() + startLine = self.LineFromChar(pos) + self.GetDocument().MarkerToggle(startLine + 1, MARKER_BOOKMARK) + return 0 + + def GotoNextBookmarkEvent(self, event, fromPos=-1): + """Move to the next bookmark""" + if fromPos == -1: + fromPos, end = self.GetSel() + startLine = self.LineFromChar(fromPos) + 1 # Zero based line to start + nextLine = self.GetDocument().MarkerGetNext(startLine + 1, MARKER_BOOKMARK) - 1 + if nextLine < 0: + nextLine = self.GetDocument().MarkerGetNext(0, MARKER_BOOKMARK) - 1 + if nextLine < 0 or nextLine == startLine - 1: + win32api.MessageBeep() + else: + self.SCIEnsureVisible(nextLine) + self.SCIGotoLine(nextLine) + return 0 + + def TabKeyEvent(self, event): + """Insert an indent. If no selection, a single indent, otherwise a block indent""" + # Handle auto-complete first. + if self.SCIAutoCActive(): + self.SCIAutoCComplete() + return 0 + # Call the IDLE event. + return self.bindings.fire("<>", event) + + def EnterKeyEvent(self, event): + """Handle the enter key with special handling for auto-complete""" + # Handle auto-complete first. + if self.SCIAutoCActive(): + self.SCIAutoCComplete() + self.SCIAutoCCancel() + # Call the IDLE event. + return self.bindings.fire("<>", event) + + def ShowInteractiveWindowEvent(self, event): + import pywin.framework.interact + + pywin.framework.interact.ShowInteractiveWindow() + + def FoldTopLevelEvent(self, event=None): + if not self.bFolding: + return 1 + + win32ui.DoWaitCursor(1) + try: + self.Colorize() + maxLine = self.GetLineCount() + # Find the first line, and check out its state. + for lineSeek in range(maxLine): + if self.SCIGetFoldLevel(lineSeek) & scintillacon.SC_FOLDLEVELHEADERFLAG: + expanding = not self.SCIGetFoldExpanded(lineSeek) + break + else: + # no folds here! + return + for lineSeek in range(lineSeek, maxLine): + level = self.SCIGetFoldLevel(lineSeek) + level_no = ( + level + & scintillacon.SC_FOLDLEVELNUMBERMASK + - scintillacon.SC_FOLDLEVELBASE + ) + is_header = level & scintillacon.SC_FOLDLEVELHEADERFLAG + # print lineSeek, level_no, is_header + if level_no == 0 and is_header: + if (expanding and not self.SCIGetFoldExpanded(lineSeek)) or ( + not expanding and self.SCIGetFoldExpanded(lineSeek) + ): + self.SCIToggleFold(lineSeek) + finally: + win32ui.DoWaitCursor(-1) + + def FoldExpandSecondLevelEvent(self, event): + if not self.bFolding: + return 1 + win32ui.DoWaitCursor(1) + ## I think this is needed since Scintilla may not have + ## already formatted parts of file outside visible window. + self.Colorize() + levels = [scintillacon.SC_FOLDLEVELBASE] + ## Scintilla's level number is based on amount of whitespace indentation + for lineno in range(self.GetLineCount()): + level = self.SCIGetFoldLevel(lineno) + if not level & scintillacon.SC_FOLDLEVELHEADERFLAG: + continue + curr_level = level & scintillacon.SC_FOLDLEVELNUMBERMASK + if curr_level > levels[-1]: + levels.append(curr_level) + try: + level_ind = levels.index(curr_level) + except ValueError: + ## probably syntax error in source file, bail + break + levels = levels[: level_ind + 1] + if level_ind == 1 and not self.SCIGetFoldExpanded(lineno): + self.SCIToggleFold(lineno) + win32ui.DoWaitCursor(-1) + + def FoldCollapseSecondLevelEvent(self, event): + if not self.bFolding: + return 1 + win32ui.DoWaitCursor(1) + ## I think this is needed since Scintilla may not have + ## already formatted parts of file outside visible window. + self.Colorize() + levels = [scintillacon.SC_FOLDLEVELBASE] + ## Scintilla's level number is based on amount of whitespace indentation + for lineno in range(self.GetLineCount()): + level = self.SCIGetFoldLevel(lineno) + if not level & scintillacon.SC_FOLDLEVELHEADERFLAG: + continue + curr_level = level & scintillacon.SC_FOLDLEVELNUMBERMASK + if curr_level > levels[-1]: + levels.append(curr_level) + try: + level_ind = levels.index(curr_level) + except ValueError: + ## probably syntax error in source file, bail + break + levels = levels[: level_ind + 1] + if level_ind == 1 and self.SCIGetFoldExpanded(lineno): + self.SCIToggleFold(lineno) + win32ui.DoWaitCursor(-1) + + def FoldExpandEvent(self, event): + if not self.bFolding: + return 1 + win32ui.DoWaitCursor(1) + lineno = self.LineFromChar(self.GetSel()[0]) + if self.SCIGetFoldLevel( + lineno + ) & scintillacon.SC_FOLDLEVELHEADERFLAG and not self.SCIGetFoldExpanded(lineno): + self.SCIToggleFold(lineno) + win32ui.DoWaitCursor(-1) + + def FoldExpandAllEvent(self, event): + if not self.bFolding: + return 1 + win32ui.DoWaitCursor(1) + for lineno in range(0, self.GetLineCount()): + if self.SCIGetFoldLevel( + lineno + ) & scintillacon.SC_FOLDLEVELHEADERFLAG and not self.SCIGetFoldExpanded( + lineno + ): + self.SCIToggleFold(lineno) + win32ui.DoWaitCursor(-1) + + def FoldCollapseEvent(self, event): + if not self.bFolding: + return 1 + win32ui.DoWaitCursor(1) + lineno = self.LineFromChar(self.GetSel()[0]) + if self.SCIGetFoldLevel( + lineno + ) & scintillacon.SC_FOLDLEVELHEADERFLAG and self.SCIGetFoldExpanded(lineno): + self.SCIToggleFold(lineno) + win32ui.DoWaitCursor(-1) + + def FoldCollapseAllEvent(self, event): + if not self.bFolding: + return 1 + win32ui.DoWaitCursor(1) + self.Colorize() + for lineno in range(0, self.GetLineCount()): + if self.SCIGetFoldLevel( + lineno + ) & scintillacon.SC_FOLDLEVELHEADERFLAG and self.SCIGetFoldExpanded(lineno): + self.SCIToggleFold(lineno) + win32ui.DoWaitCursor(-1) + + +from pywin.framework.editor.frame import EditorFrame + + +class SplitterFrame(EditorFrame): + def OnCreate(self, cs): + self.HookCommand(self.OnWindowSplit, win32ui.ID_WINDOW_SPLIT) + return 1 + + def OnWindowSplit(self, id, code): + self.GetDlgItem(win32ui.AFX_IDW_PANE_FIRST).DoKeyboardSplit() + return 1 + + +from pywin.framework.editor.template import EditorTemplateBase + + +class SyntEditTemplate(EditorTemplateBase): + def __init__( + self, res=win32ui.IDR_TEXTTYPE, makeDoc=None, makeFrame=None, makeView=None + ): + if makeDoc is None: + makeDoc = SyntEditDocument + if makeView is None: + makeView = SyntEditView + if makeFrame is None: + makeFrame = SplitterFrame + self.bSetMenus = 0 + EditorTemplateBase.__init__(self, res, makeDoc, makeFrame, makeView) + + def CheckIDLEMenus(self, idle): + if self.bSetMenus: + return + self.bSetMenus = 1 + + submenu = win32ui.CreatePopupMenu() + newitems = idle.GetMenuItems("edit") + flags = win32con.MF_STRING | win32con.MF_ENABLED + for text, event in newitems: + id = bindings.event_to_commands.get(event) + if id is not None: + keyname = pywin.scintilla.view.configManager.get_key_binding( + event, ["editor"] + ) + if keyname is not None: + text = text + "\t" + keyname + submenu.AppendMenu(flags, id, text) + + mainMenu = self.GetSharedMenu() + editMenu = mainMenu.GetSubMenu(1) + editMenu.AppendMenu(win32con.MF_SEPARATOR, 0, "") + editMenu.AppendMenu( + win32con.MF_STRING | win32con.MF_POPUP | win32con.MF_ENABLED, + submenu.GetHandle(), + "&Source Code", + ) + + def _CreateDocTemplate(self, resourceId): + return win32ui.CreateDocTemplate(resourceId) + + def CreateWin32uiDocument(self): + return self.DoCreateDoc() + + def GetPythonPropertyPages(self): + """Returns a list of property pages""" + from pywin.scintilla import configui + + return EditorTemplateBase.GetPythonPropertyPages(self) + [ + configui.ScintillaFormatPropertyPage() + ] + + +# For debugging purposes, when this module may be reloaded many times. +try: + win32ui.GetApp().RemoveDocTemplate(editorTemplate) +except NameError: + pass + +editorTemplate = SyntEditTemplate() +win32ui.GetApp().AddDocTemplate(editorTemplate) diff --git a/myenv/Lib/site-packages/pythonwin/pywin/framework/editor/configui.py b/myenv/Lib/site-packages/pythonwin/pywin/framework/editor/configui.py new file mode 100644 index 000000000..903ad66c7 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/framework/editor/configui.py @@ -0,0 +1,308 @@ +import pywin.scintilla.config +import win32api +import win32con +import win32ui +from pywin.framework.editor import ( + DeleteEditorOption, + GetEditorFontOption, + GetEditorOption, + SetEditorFontOption, + SetEditorOption, + defaultCharacterFormat, + editorTemplate, +) +from pywin.mfc import dialog + +from . import document + +# The standard 16 color VGA palette should always be possible +paletteVGA = ( + ("Black", 0, 0, 0), + ("Navy", 0, 0, 128), + ("Green", 0, 128, 0), + ("Cyan", 0, 128, 128), + ("Maroon", 128, 0, 0), + ("Purple", 128, 0, 128), + ("Olive", 128, 128, 0), + ("Gray", 128, 128, 128), + ("Silver", 192, 192, 192), + ("Blue", 0, 0, 255), + ("Lime", 0, 255, 0), + ("Aqua", 0, 255, 255), + ("Red", 255, 0, 0), + ("Fuchsia", 255, 0, 255), + ("Yellow", 255, 255, 0), + ("White", 255, 255, 255), +) + + +###################################################### +# +# Property Page for editor options +# +class EditorPropertyPage(dialog.PropertyPage): + def __init__(self): + dialog.PropertyPage.__init__(self, win32ui.IDD_PP_EDITOR) + self.autooptions = [] + self._AddEditorOption(win32ui.IDC_AUTO_RELOAD, "i", "Auto Reload", 1) + self._AddEditorOption( + win32ui.IDC_COMBO1, "i", "Backup Type", document.BAK_DOT_BAK_BAK_DIR + ) + self._AddEditorOption( + win32ui.IDC_AUTOCOMPLETE, "i", "Autocomplete Attributes", 1 + ) + self._AddEditorOption(win32ui.IDC_CALLTIPS, "i", "Show Call Tips", 1) + self._AddEditorOption( + win32ui.IDC_MARGIN_LINENUMBER, "i", "Line Number Margin Width", 0 + ) + self._AddEditorOption(win32ui.IDC_RADIO1, "i", "MarkersInMargin", None) + self._AddEditorOption( + win32ui.IDC_MARGIN_MARKER, "i", "Marker Margin Width", None + ) + self["Marker Margin Width"] = GetEditorOption("Marker Margin Width", 16) + + # Folding + self._AddEditorOption(win32ui.IDC_MARGIN_FOLD, "i", "Fold Margin Width", 12) + self._AddEditorOption(win32ui.IDC_FOLD_ENABLE, "i", "Enable Folding", 1) + self._AddEditorOption(win32ui.IDC_FOLD_ON_OPEN, "i", "Fold On Open", 0) + self._AddEditorOption(win32ui.IDC_FOLD_SHOW_LINES, "i", "Fold Lines", 1) + + # Right edge. + self._AddEditorOption( + win32ui.IDC_RIGHTEDGE_ENABLE, "i", "Right Edge Enabled", 0 + ) + self._AddEditorOption( + win32ui.IDC_RIGHTEDGE_COLUMN, "i", "Right Edge Column", 75 + ) + + # Source control, etc + self.AddDDX(win32ui.IDC_VSS_INTEGRATE, "bVSS") + self.AddDDX(win32ui.IDC_KEYBOARD_CONFIG, "Configs", "l") + self["Configs"] = pywin.scintilla.config.find_config_files() + + def _AddEditorOption(self, idd, typ, optionName, defaultVal): + self.AddDDX(idd, optionName, typ) + # some options are "derived" - ie, can be implied from others + # (eg, "view markers in background" is implied from "markerMarginWidth==0" + # So we don't actually store these values, but they do still get DDX support. + if defaultVal is not None: + self[optionName] = GetEditorOption(optionName, defaultVal) + self.autooptions.append((optionName, defaultVal)) + + def OnInitDialog(self): + for name, val in self.autooptions: + self[name] = GetEditorOption(name, val) + + # Note that these MUST be in the same order as the BAK constants. + cbo = self.GetDlgItem(win32ui.IDC_COMBO1) + cbo.AddString("None") + cbo.AddString(".BAK File") + cbo.AddString("TEMP dir") + cbo.AddString("Own dir") + + # Source Safe + bVSS = ( + GetEditorOption("Source Control Module", "") == "pywin.framework.editor.vss" + ) + self["bVSS"] = bVSS + + edit = self.GetDlgItem(win32ui.IDC_RIGHTEDGE_SAMPLE) + edit.SetWindowText("Sample Color") + + rc = dialog.PropertyPage.OnInitDialog(self) + + try: + self.GetDlgItem(win32ui.IDC_KEYBOARD_CONFIG).SelectString( + -1, GetEditorOption("Keyboard Config", "default") + ) + except win32ui.error: + import traceback + + traceback.print_exc() + pass + + self.HookCommand(self.OnButSimple, win32ui.IDC_FOLD_ENABLE) + self.HookCommand(self.OnButSimple, win32ui.IDC_RADIO1) + self.HookCommand(self.OnButSimple, win32ui.IDC_RADIO2) + self.HookCommand(self.OnButSimple, win32ui.IDC_RIGHTEDGE_ENABLE) + self.HookCommand(self.OnButEdgeColor, win32ui.IDC_RIGHTEDGE_DEFINE) + + butMarginEnabled = self["Marker Margin Width"] > 0 + self.GetDlgItem(win32ui.IDC_RADIO1).SetCheck(butMarginEnabled) + self.GetDlgItem(win32ui.IDC_RADIO2).SetCheck(not butMarginEnabled) + + self.edgeColor = self.initialEdgeColor = GetEditorOption( + "Right Edge Color", win32api.RGB(0xEF, 0xEF, 0xEF) + ) + for spinner_id in (win32ui.IDC_SPIN1, win32ui.IDC_SPIN2, win32ui.IDC_SPIN3): + spinner = self.GetDlgItem(spinner_id) + spinner.SetRange(0, 100) + self.UpdateUIForState() + + return rc + + def OnButSimple(self, id, code): + if code == win32con.BN_CLICKED: + self.UpdateUIForState() + + def OnButEdgeColor(self, id, code): + if code == win32con.BN_CLICKED: + d = win32ui.CreateColorDialog(self.edgeColor, 0, self) + # Ensure the current color is a custom color (as it may not be in the swatch) + # plus some other nice gray scales. + ccs = [self.edgeColor] + for c in range(0xEF, 0x4F, -0x10): + ccs.append(win32api.RGB(c, c, c)) + d.SetCustomColors(ccs) + if d.DoModal() == win32con.IDOK: + self.edgeColor = d.GetColor() + self.UpdateUIForState() + + def UpdateUIForState(self): + folding = self.GetDlgItem(win32ui.IDC_FOLD_ENABLE).GetCheck() + self.GetDlgItem(win32ui.IDC_FOLD_ON_OPEN).EnableWindow(folding) + self.GetDlgItem(win32ui.IDC_FOLD_SHOW_LINES).EnableWindow(folding) + + widthEnabled = self.GetDlgItem(win32ui.IDC_RADIO1).GetCheck() + self.GetDlgItem(win32ui.IDC_MARGIN_MARKER).EnableWindow(widthEnabled) + self.UpdateData() # Ensure self[] is up to date with the control data. + if widthEnabled and self["Marker Margin Width"] == 0: + self["Marker Margin Width"] = 16 + self.UpdateData(0) # Ensure control up to date with self[] + + # Right edge + edgeEnabled = self.GetDlgItem(win32ui.IDC_RIGHTEDGE_ENABLE).GetCheck() + self.GetDlgItem(win32ui.IDC_RIGHTEDGE_COLUMN).EnableWindow(edgeEnabled) + self.GetDlgItem(win32ui.IDC_RIGHTEDGE_SAMPLE).EnableWindow(edgeEnabled) + self.GetDlgItem(win32ui.IDC_RIGHTEDGE_DEFINE).EnableWindow(edgeEnabled) + + edit = self.GetDlgItem(win32ui.IDC_RIGHTEDGE_SAMPLE) + edit.SetBackgroundColor(0, self.edgeColor) + + def OnOK(self): + for name, defVal in self.autooptions: + SetEditorOption(name, self[name]) + # Margin width gets handled differently. + if self["MarkersInMargin"] == 0: + SetEditorOption("Marker Margin Width", self["Marker Margin Width"]) + else: + SetEditorOption("Marker Margin Width", 0) + if self.edgeColor != self.initialEdgeColor: + SetEditorOption("Right Edge Color", self.edgeColor) + if self["bVSS"]: + SetEditorOption("Source Control Module", "pywin.framework.editor.vss") + else: + if ( + GetEditorOption("Source Control Module", "") + == "pywin.framework.editor.vss" + ): + SetEditorOption("Source Control Module", "") + # Keyboard config + configname = self.GetDlgItem(win32ui.IDC_KEYBOARD_CONFIG).GetWindowText() + if configname: + if configname == "default": + DeleteEditorOption("Keyboard Config") + else: + SetEditorOption("Keyboard Config", configname) + + import pywin.scintilla.view + + pywin.scintilla.view.LoadConfiguration() + + # Now tell all views we have changed. + ## for doc in editorTemplate.GetDocumentList(): + ## for view in doc.GetAllViews(): + ## try: + ## fn = view.OnConfigChange + ## except AttributeError: + ## continue + ## fn() + return 1 + + +class EditorWhitespacePropertyPage(dialog.PropertyPage): + def __init__(self): + dialog.PropertyPage.__init__(self, win32ui.IDD_PP_TABS) + self.autooptions = [] + self._AddEditorOption(win32ui.IDC_TAB_SIZE, "i", "Tab Size", 4) + self._AddEditorOption(win32ui.IDC_INDENT_SIZE, "i", "Indent Size", 4) + self._AddEditorOption(win32ui.IDC_USE_SMART_TABS, "i", "Smart Tabs", 1) + self._AddEditorOption(win32ui.IDC_VIEW_WHITESPACE, "i", "View Whitespace", 0) + self._AddEditorOption(win32ui.IDC_VIEW_EOL, "i", "View EOL", 0) + self._AddEditorOption( + win32ui.IDC_VIEW_INDENTATIONGUIDES, "i", "View Indentation Guides", 0 + ) + + def _AddEditorOption(self, idd, typ, optionName, defaultVal): + self.AddDDX(idd, optionName, typ) + self[optionName] = GetEditorOption(optionName, defaultVal) + self.autooptions.append((optionName, defaultVal)) + + def OnInitDialog(self): + for name, val in self.autooptions: + self[name] = GetEditorOption(name, val) + + rc = dialog.PropertyPage.OnInitDialog(self) + + idc = win32ui.IDC_TABTIMMY_NONE + if GetEditorOption("Use Tab Timmy", 1): + idc = win32ui.IDC_TABTIMMY_IND + self.GetDlgItem(idc).SetCheck(1) + + idc = win32ui.IDC_RADIO1 + if GetEditorOption("Use Tabs", 0): + idc = win32ui.IDC_USE_TABS + self.GetDlgItem(idc).SetCheck(1) + + tt_color = GetEditorOption("Tab Timmy Color", win32api.RGB(0xFF, 0, 0)) + self.cbo = self.GetDlgItem(win32ui.IDC_COMBO1) + for c in paletteVGA: + self.cbo.AddString(c[0]) + sel = 0 + for c in paletteVGA: + if tt_color == win32api.RGB(c[1], c[2], c[3]): + break + sel = sel + 1 + else: + sel = -1 + self.cbo.SetCurSel(sel) + self.HookCommand(self.OnButSimple, win32ui.IDC_TABTIMMY_NONE) + self.HookCommand(self.OnButSimple, win32ui.IDC_TABTIMMY_IND) + self.HookCommand(self.OnButSimple, win32ui.IDC_TABTIMMY_BG) + # Set ranges for the spinners. + for spinner_id in [win32ui.IDC_SPIN1, win32ui.IDC_SPIN2]: + spinner = self.GetDlgItem(spinner_id) + spinner.SetRange(1, 16) + return rc + + def OnButSimple(self, id, code): + if code == win32con.BN_CLICKED: + self.UpdateUIForState() + + def UpdateUIForState(self): + timmy = self.GetDlgItem(win32ui.IDC_TABTIMMY_NONE).GetCheck() + self.GetDlgItem(win32ui.IDC_COMBO1).EnableWindow(not timmy) + + def OnOK(self): + for name, defVal in self.autooptions: + SetEditorOption(name, self[name]) + + SetEditorOption("Use Tabs", self.GetDlgItem(win32ui.IDC_USE_TABS).GetCheck()) + + SetEditorOption( + "Use Tab Timmy", self.GetDlgItem(win32ui.IDC_TABTIMMY_IND).GetCheck() + ) + c = paletteVGA[self.cbo.GetCurSel()] + SetEditorOption("Tab Timmy Color", win32api.RGB(c[1], c[2], c[3])) + + return 1 + + +def testpp(): + ps = dialog.PropertySheet("Editor Options") + ps.AddPage(EditorWhitespacePropertyPage()) + ps.DoModal() + + +if __name__ == "__main__": + testpp() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/framework/editor/document.py b/myenv/Lib/site-packages/pythonwin/pywin/framework/editor/document.py new file mode 100644 index 000000000..e66947b81 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/framework/editor/document.py @@ -0,0 +1,378 @@ +# We no longer support the old, non-colour editor! + +import os +import shutil +import traceback + +import win32api +import win32con +import win32ui +from pywin.framework.editor import GetEditorOption +from pywin.mfc import docview, object + +BAK_NONE = 0 +BAK_DOT_BAK = 1 +BAK_DOT_BAK_TEMP_DIR = 2 +BAK_DOT_BAK_BAK_DIR = 3 + +MSG_CHECK_EXTERNAL_FILE = ( + win32con.WM_USER + 1999 +) ## WARNING: Duplicated in editor.py and coloreditor.py + +import pywin.scintilla.document + +ParentEditorDocument = pywin.scintilla.document.CScintillaDocument + + +class EditorDocumentBase(ParentEditorDocument): + def __init__(self, template): + self.bAutoReload = GetEditorOption("Auto Reload", 1) + self.bDeclinedReload = 0 # Has the user declined to reload. + self.fileStat = None + self.bReportedFileNotFound = 0 + + # what sort of bak file should I create. + # default to write to %temp%/bak/filename.ext + self.bakFileType = GetEditorOption("Backup Type", BAK_DOT_BAK_BAK_DIR) + + self.watcherThread = FileWatchingThread(self) + self.watcherThread.CreateThread() + # Should I try and use VSS integration? + self.scModuleName = GetEditorOption("Source Control Module", "") + self.scModule = None # Loaded when first used. + ParentEditorDocument.__init__(self, template, template.CreateWin32uiDocument()) + + def OnCloseDocument(self): + self.watcherThread.SignalStop() + return self._obj_.OnCloseDocument() + + # def OnOpenDocument(self, name): + # rc = ParentEditorDocument.OnOpenDocument(self, name) + # self.GetFirstView()._SetLoadedText(self.text) + # self._DocumentStateChanged() + # return rc + + def OnSaveDocument(self, fileName): + win32ui.SetStatusText("Saving file...", 1) + # rename to bak if required. + dir, basename = os.path.split(fileName) + if self.bakFileType == BAK_DOT_BAK: + bakFileName = dir + "\\" + os.path.splitext(basename)[0] + ".bak" + elif self.bakFileType == BAK_DOT_BAK_TEMP_DIR: + bakFileName = ( + win32api.GetTempPath() + "\\" + os.path.splitext(basename)[0] + ".bak" + ) + elif self.bakFileType == BAK_DOT_BAK_BAK_DIR: + tempPath = os.path.join(win32api.GetTempPath(), "bak") + try: + os.mkdir(tempPath, 0) + except os.error: + pass + bakFileName = os.path.join(tempPath, basename) + try: + os.unlink(bakFileName) # raise NameError if no bakups wanted. + except (os.error, NameError): + pass + try: + # Do a copy as it might be on different volumes, + # and the file may be a hard-link, causing the link + # to follow the backup. + shutil.copy2(fileName, bakFileName) + except (os.error, NameError, IOError): + pass + try: + self.SaveFile(fileName) + except IOError as details: + win32ui.MessageBox("Error - could not save file\r\n\r\n%s" % details) + return 0 + except (UnicodeEncodeError, LookupError) as details: + rc = win32ui.MessageBox( + "Encoding failed: \r\n%s" % details + + "\r\nPlease add desired source encoding as first line of file, eg \r\n" + + "# -*- coding: mbcs -*-\r\n\r\n" + + "If you continue, the file will be saved as binary and will\r\n" + + "not be valid in the declared encoding.\r\n\r\n" + + "Save the file as binary with an invalid encoding?", + "File save failed", + win32con.MB_YESNO | win32con.MB_DEFBUTTON2, + ) + if rc == win32con.IDYES: + try: + self.SaveFile(fileName, encoding="latin-1") + except IOError as details: + win32ui.MessageBox( + "Error - could not save file\r\n\r\n%s" % details + ) + return 0 + else: + return 0 + self.SetModifiedFlag(0) # No longer dirty + self.bDeclinedReload = 0 # They probably want to know if it changes again! + win32ui.AddToRecentFileList(fileName) + self.SetPathName(fileName) + win32ui.SetStatusText("Ready") + self._DocumentStateChanged() + return 1 + + def FinalizeViewCreation(self, view): + ParentEditorDocument.FinalizeViewCreation(self, view) + if view == self.GetFirstView(): + self._DocumentStateChanged() + if view.bFolding and GetEditorOption("Fold On Open", 0): + view.FoldTopLevelEvent() + + def HookViewNotifications(self, view): + ParentEditorDocument.HookViewNotifications(self, view) + + # Support for reloading the document from disk - presumably after some + # external application has modified it (or possibly source control has + # checked it out. + def ReloadDocument(self): + """Reloads the document from disk. Assumes the file has + been saved and user has been asked if necessary - it just does it! + """ + win32ui.SetStatusText("Reloading document. Please wait...", 1) + self.SetModifiedFlag(0) + # Loop over all views, saving their state, then reload the document + views = self.GetAllViews() + states = [] + for view in views: + try: + info = view._PrepareUserStateChange() + except AttributeError: # Not our editor view? + info = None + states.append(info) + self.OnOpenDocument(self.GetPathName()) + for view, info in zip(views, states): + if info is not None: + view._EndUserStateChange(info) + self._DocumentStateChanged() + win32ui.SetStatusText("Document reloaded.") + + # Reloading the file + def CheckExternalDocumentUpdated(self): + if self.bDeclinedReload or not self.GetPathName(): + return + try: + newstat = os.stat(self.GetPathName()) + except os.error as exc: + if not self.bReportedFileNotFound: + print( + "The file '%s' is open for editing, but\nchecking it for changes caused the error: %s" + % (self.GetPathName(), exc.strerror) + ) + self.bReportedFileNotFound = 1 + return + if self.bReportedFileNotFound: + print( + "The file '%s' has re-appeared - continuing to watch for changes..." + % (self.GetPathName(),) + ) + self.bReportedFileNotFound = ( + 0 # Once found again we want to start complaining. + ) + changed = ( + (self.fileStat is None) + or self.fileStat[0] != newstat[0] + or self.fileStat[6] != newstat[6] + or self.fileStat[8] != newstat[8] + or self.fileStat[9] != newstat[9] + ) + if changed: + question = None + if self.IsModified(): + question = ( + "%s\r\n\r\nThis file has been modified outside of the source editor.\r\nDo you want to reload it and LOSE THE CHANGES in the source editor?" + % self.GetPathName() + ) + mbStyle = win32con.MB_YESNO | win32con.MB_DEFBUTTON2 # Default to "No" + else: + if not self.bAutoReload: + question = ( + "%s\r\n\r\nThis file has been modified outside of the source editor.\r\nDo you want to reload it?" + % self.GetPathName() + ) + mbStyle = win32con.MB_YESNO # Default to "Yes" + if question: + rc = win32ui.MessageBox(question, None, mbStyle) + if rc != win32con.IDYES: + self.bDeclinedReload = 1 + return + self.ReloadDocument() + + def _DocumentStateChanged(self): + """Called whenever the documents state (on disk etc) has been changed + by the editor (eg, as the result of a save operation) + """ + if self.GetPathName(): + try: + self.fileStat = os.stat(self.GetPathName()) + except os.error: + self.fileStat = None + else: + self.fileStat = None + self.watcherThread._DocumentStateChanged() + self._UpdateUIForState() + self._ApplyOptionalToViews("_UpdateUIForState") + self._ApplyOptionalToViews("SetReadOnly", self._IsReadOnly()) + self._ApplyOptionalToViews("SCISetSavePoint") + # Allow the debugger to reset us too. + import pywin.debugger + + if pywin.debugger.currentDebugger is not None: + pywin.debugger.currentDebugger.UpdateDocumentLineStates(self) + + # Read-only document support - make it obvious to the user + # that the file is read-only. + def _IsReadOnly(self): + return self.fileStat is not None and (self.fileStat[0] & 128) == 0 + + def _UpdateUIForState(self): + """Change the title to reflect the state of the document - + eg ReadOnly, Dirty, etc + """ + filename = self.GetPathName() + if not filename: + return # New file - nothing to do + try: + # This seems necessary so the internal state of the window becomes + # "visible". without it, it is still shown, but certain functions + # (such as updating the title) dont immediately work? + self.GetFirstView().ShowWindow(win32con.SW_SHOW) + title = win32ui.GetFileTitle(filename) + except win32ui.error: + title = filename + if self._IsReadOnly(): + title = title + " (read-only)" + self.SetTitle(title) + + def MakeDocumentWritable(self): + pretend_ss = 0 # Set to 1 to test this without source safe :-) + if not self.scModuleName and not pretend_ss: # No Source Control support. + win32ui.SetStatusText( + "Document is read-only, and no source-control system is configured" + ) + win32api.MessageBeep() + return 0 + + # We have source control support - check if the user wants to use it. + msg = "Would you like to check this file out?" + defButton = win32con.MB_YESNO + if self.IsModified(): + msg = msg + "\r\n\r\nALL CHANGES IN THE EDITOR WILL BE LOST" + defButton = win32con.MB_YESNO + if win32ui.MessageBox(msg, None, defButton) != win32con.IDYES: + return 0 + + if pretend_ss: + print("We are only pretending to check it out!") + win32api.SetFileAttributes( + self.GetPathName(), win32con.FILE_ATTRIBUTE_NORMAL + ) + self.ReloadDocument() + return 1 + + # Now call on the module to do it. + if self.scModule is None: + try: + self.scModule = __import__(self.scModuleName) + for part in self.scModuleName.split(".")[1:]: + self.scModule = getattr(self.scModule, part) + except: + traceback.print_exc() + print("Error loading source control module.") + return 0 + + if self.scModule.CheckoutFile(self.GetPathName()): + self.ReloadDocument() + return 1 + return 0 + + def CheckMakeDocumentWritable(self): + if self._IsReadOnly(): + return self.MakeDocumentWritable() + return 1 + + def SaveModified(self): + # Called as the document is closed. If we are about + # to prompt for a save, bring the document to the foreground. + if self.IsModified(): + frame = self.GetFirstView().GetParentFrame() + try: + frame.MDIActivate() + frame.AutoRestore() + except: + print("Could not bring document to foreground") + return self._obj_.SaveModified() + + +# NOTE - I DONT use the standard threading module, +# as this waits for all threads to terminate at shutdown. +# When using the debugger, it is possible shutdown will +# occur without Pythonwin getting a complete shutdown, +# so we deadlock at the end - threading is waiting for +import pywin.mfc.thread +import win32event + + +class FileWatchingThread(pywin.mfc.thread.WinThread): + def __init__(self, doc): + self.doc = doc + self.adminEvent = win32event.CreateEvent(None, 0, 0, None) + self.stopEvent = win32event.CreateEvent(None, 0, 0, None) + self.watchEvent = None + pywin.mfc.thread.WinThread.__init__(self) + + def _DocumentStateChanged(self): + win32event.SetEvent(self.adminEvent) + + def RefreshEvent(self): + self.hwnd = self.doc.GetFirstView().GetSafeHwnd() + if self.watchEvent is not None: + win32api.FindCloseChangeNotification(self.watchEvent) + self.watchEvent = None + path = self.doc.GetPathName() + if path: + path = os.path.dirname(path) + if path: + filter = ( + win32con.FILE_NOTIFY_CHANGE_FILE_NAME + | win32con.FILE_NOTIFY_CHANGE_ATTRIBUTES + | win32con.FILE_NOTIFY_CHANGE_LAST_WRITE + ) + try: + self.watchEvent = win32api.FindFirstChangeNotification(path, 0, filter) + except win32api.error as exc: + print("Can not watch file", path, "for changes -", exc.strerror) + + def SignalStop(self): + win32event.SetEvent(self.stopEvent) + + def Run(self): + while 1: + handles = [self.stopEvent, self.adminEvent] + if self.watchEvent is not None: + handles.append(self.watchEvent) + rc = win32event.WaitForMultipleObjects(handles, 0, win32event.INFINITE) + if rc == win32event.WAIT_OBJECT_0: + break + elif rc == win32event.WAIT_OBJECT_0 + 1: + self.RefreshEvent() + else: + win32api.PostMessage(self.hwnd, MSG_CHECK_EXTERNAL_FILE, 0, 0) + try: + # If the directory has been removed underneath us, we get this error. + win32api.FindNextChangeNotification(self.watchEvent) + except win32api.error as exc: + print( + "Can not watch file", + self.doc.GetPathName(), + "for changes -", + exc.strerror, + ) + break + + # close a circular reference + self.doc = None + if self.watchEvent: + win32api.FindCloseChangeNotification(self.watchEvent) diff --git a/myenv/Lib/site-packages/pythonwin/pywin/framework/editor/editor.py b/myenv/Lib/site-packages/pythonwin/pywin/framework/editor/editor.py new file mode 100644 index 000000000..aa1f53857 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/framework/editor/editor.py @@ -0,0 +1,516 @@ +##################################################################### +# +# editor.py +# +# A general purpose text editor, built on top of the win32ui edit +# type, which is built on an MFC CEditView +# +# +# We now support reloading of externally modified documented +# (eg, presumably by some other process, such as source control or +# another editor. +# We also suport auto-loading of externally modified files. +# - if the current document has not been modified in this +# editor, but has been modified on disk, then the file +# can be automatically reloaded. +# +# Note that it will _always_ prompt you if the file in the editor has been modified. + + +import re + +import regex +import win32api +import win32con +import win32ui +from pywin.framework.editor import ( + GetEditorFontOption, + GetEditorOption, + SetEditorFontOption, + SetEditorOption, + defaultCharacterFormat, +) +from pywin.mfc import afxres, dialog, docview + +patImport = regex.symcomp("import \(.*\)") +patIndent = regex.compile("^\\([ \t]*[~ \t]\\)") + +ID_LOCATE_FILE = 0xE200 +ID_GOTO_LINE = 0xE2001 +MSG_CHECK_EXTERNAL_FILE = ( + win32con.WM_USER + 1999 +) ## WARNING: Duplicated in document.py and coloreditor.py + +# Key Codes that modify the bufffer when Ctrl or Alt are NOT pressed. +MODIFYING_VK_KEYS = [ + win32con.VK_BACK, + win32con.VK_TAB, + win32con.VK_RETURN, + win32con.VK_SPACE, + win32con.VK_DELETE, +] +for k in range(48, 91): + MODIFYING_VK_KEYS.append(k) + +# Key Codes that modify the bufffer when Ctrl is pressed. +MODIFYING_VK_KEYS_CTRL = [ + win32con.VK_BACK, + win32con.VK_RETURN, + win32con.VK_SPACE, + win32con.VK_DELETE, +] + +# Key Codes that modify the bufffer when Alt is pressed. +MODIFYING_VK_KEYS_ALT = [ + win32con.VK_BACK, + win32con.VK_RETURN, + win32con.VK_SPACE, + win32con.VK_DELETE, +] + + +# The editor itself starts here. +# Using the MFC Document/View model, we have an EditorDocument, which is responsible for +# managing the contents of the file, and a view which is responsible for rendering it. +# +# Due to a limitation in the Windows edit controls, we are limited to one view +# per document, although nothing in this code assumes this (I hope!) + +isRichText = 1 # We are using the Rich Text control. This has not been tested with value "0" for quite some time! + +# ParentEditorDocument=docview.Document +from .document import EditorDocumentBase + +ParentEditorDocument = EditorDocumentBase + + +class EditorDocument(ParentEditorDocument): + # + # File loading and saving operations + # + def OnOpenDocument(self, filename): + # + # handle Unix and PC text file format. + # + + # Get the "long name" of the file name, as it may have been translated + # to short names by the shell. + self.SetPathName(filename) # Must set this early! + # Now do the work! + self.BeginWaitCursor() + win32ui.SetStatusText("Loading file...", 1) + try: + f = open(filename, "rb") + except IOError: + win32ui.MessageBox( + filename + + "\nCan not find this file\nPlease verify that the correct path and file name are given" + ) + self.EndWaitCursor() + return 0 + raw = f.read() + f.close() + contents = self.TranslateLoadedData(raw) + rc = 0 + try: + self.GetFirstView().SetWindowText(contents) + rc = 1 + except TypeError: # Null byte in file. + win32ui.MessageBox("This file contains NULL bytes, and can not be edited") + rc = 0 + + self.EndWaitCursor() + self.SetModifiedFlag(0) # No longer dirty + self._DocumentStateChanged() + return rc + + def TranslateLoadedData(self, data): + """Given raw data read from a file, massage it suitable for the edit window""" + # if a CR in the first 250 chars, then perform the expensive translate + if data[:250].find("\r") == -1: + win32ui.SetStatusText( + "Translating from Unix file format - please wait...", 1 + ) + return re.sub("\r*\n", "\r\n", data) + else: + return data + + def SaveFile(self, fileName, encoding=None): + if isRichText: + view = self.GetFirstView() + view.SaveTextFile(fileName, encoding=encoding) + else: # Old style edit view window. + self.GetFirstView().SaveFile(fileName) + try: + # Make sure line cache has updated info about me! + import linecache + + linecache.checkcache() + except: + pass + + # + # Color state stuff + # + def SetAllLineColors(self, color=None): + for view in self.GetAllViews(): + view.SetAllLineColors(color) + + def SetLineColor(self, lineNo, color): + "Color a line of all views" + for view in self.GetAllViews(): + view.SetLineColor(lineNo, color) + + +# def StreamTextOut(self, data): ### This seems unreliable??? +# self.saveFileHandle.write(data) +# return 1 # keep em coming! + +# ParentEditorView=docview.EditView +ParentEditorView = docview.RichEditView + + +class EditorView(ParentEditorView): + def __init__(self, doc): + ParentEditorView.__init__(self, doc) + if isRichText: + self.SetWordWrap(win32ui.CRichEditView_WrapNone) + + self.addToMRU = 1 + self.HookHandlers() + self.bCheckingFile = 0 + + self.defCharFormat = GetEditorFontOption("Default Font", defaultCharacterFormat) + + # Smart tabs override everything else if context can be worked out. + self.bSmartTabs = GetEditorOption("Smart Tabs", 1) + + self.tabSize = GetEditorOption("Tab Size", 8) + self.indentSize = GetEditorOption("Indent Size", 8) + # If next indent is at a tab position, and useTabs is set, a tab will be inserted. + self.bUseTabs = GetEditorOption("Use Tabs", 1) + + def OnInitialUpdate(self): + rc = self._obj_.OnInitialUpdate() + self.SetDefaultCharFormat(self.defCharFormat) + return rc + + def CutCurLine(self): + curLine = self._obj_.LineFromChar() + nextLine = curLine + 1 + start = self._obj_.LineIndex(curLine) + end = self._obj_.LineIndex(nextLine) + if end == 0: # must be last line. + end = start + self.end.GetLineLength(curLine) + self._obj_.SetSel(start, end) + self._obj_.Cut() + + def _PrepareUserStateChange(self): + "Return selection, lineindex, etc info, so it can be restored" + self.SetRedraw(0) + return self.GetModify(), self.GetSel(), self.GetFirstVisibleLine() + + def _EndUserStateChange(self, info): + scrollOff = info[2] - self.GetFirstVisibleLine() + if scrollOff: + self.LineScroll(scrollOff) + self.SetSel(info[1]) + self.SetModify(info[0]) + self.SetRedraw(1) + self.InvalidateRect() + self.UpdateWindow() + + def _UpdateUIForState(self): + self.SetReadOnly(self.GetDocument()._IsReadOnly()) + + def SetAllLineColors(self, color=None): + if isRichText: + info = self._PrepareUserStateChange() + try: + if color is None: + color = self.defCharFormat[4] + self.SetSel(0, -1) + self.SetSelectionCharFormat((win32con.CFM_COLOR, 0, 0, 0, color)) + finally: + self._EndUserStateChange(info) + + def SetLineColor(self, lineNo, color): + "lineNo is the 1 based line number to set. If color is None, default color is used." + if isRichText: + info = self._PrepareUserStateChange() + try: + if color is None: + color = self.defCharFormat[4] + lineNo = lineNo - 1 + startIndex = self.LineIndex(lineNo) + if startIndex != -1: + self.SetSel(startIndex, self.LineIndex(lineNo + 1)) + self.SetSelectionCharFormat((win32con.CFM_COLOR, 0, 0, 0, color)) + finally: + self._EndUserStateChange(info) + + def Indent(self): + """Insert an indent to move the cursor to the next tab position. + + Honors the tab size and 'use tabs' settings. Assumes the cursor is already at the + position to be indented, and the selection is a single character (ie, not a block) + """ + start, end = self._obj_.GetSel() + startLine = self._obj_.LineFromChar(start) + line = self._obj_.GetLine(startLine) + realCol = start - self._obj_.LineIndex(startLine) + # Calulate the next tab stop. + # Expand existing tabs. + curCol = 0 + for ch in line[:realCol]: + if ch == "\t": + curCol = ((curCol / self.tabSize) + 1) * self.tabSize + else: + curCol = curCol + 1 + nextColumn = ((curCol / self.indentSize) + 1) * self.indentSize + # print "curCol is", curCol, "nextColumn is", nextColumn + ins = None + if self.bSmartTabs: + # Look for some context. + if realCol == 0: # Start of the line - see if the line above can tell us + lookLine = startLine - 1 + while lookLine >= 0: + check = self._obj_.GetLine(lookLine)[0:1] + if check in ("\t", " "): + ins = check + break + lookLine = lookLine - 1 + else: # See if the previous char can tell us + check = line[realCol - 1] + if check in ("\t", " "): + ins = check + + # Either smart tabs off, or not smart enough! + # Use the "old style" settings. + if ins is None: + if self.bUseTabs and nextColumn % self.tabSize == 0: + ins = "\t" + else: + ins = " " + + if ins == " ": + # Calc the number of spaces to take us to the next stop + ins = ins * (nextColumn - curCol) + + self._obj_.ReplaceSel(ins) + + def BlockDent(self, isIndent, startLine, endLine): + "Indent/Undent all lines specified" + if not self.GetDocument().CheckMakeDocumentWritable(): + return 0 + tabSize = self.tabSize # hard-code for now! + info = self._PrepareUserStateChange() + try: + for lineNo in range(startLine, endLine): + pos = self._obj_.LineIndex(lineNo) + self._obj_.SetSel(pos, pos) + if isIndent: + self.Indent() + else: + line = self._obj_.GetLine(lineNo) + try: + noToDel = 0 + if line[0] == "\t": + noToDel = 1 + elif line[0] == " ": + for noToDel in range(0, tabSize): + if line[noToDel] != " ": + break + else: + noToDel = tabSize + if noToDel: + self._obj_.SetSel(pos, pos + noToDel) + self._obj_.Clear() + except IndexError: + pass + finally: + self._EndUserStateChange(info) + self.GetDocument().SetModifiedFlag(1) # Now dirty + self._obj_.SetSel(self.LineIndex(startLine), self.LineIndex(endLine)) + + def GotoLine(self, lineNo=None): + try: + if lineNo is None: + lineNo = int(input("Enter Line Number")) + except (ValueError, KeyboardInterrupt): + return 0 + self.GetLineCount() # Seems to be needed when file first opened??? + charNo = self.LineIndex(lineNo - 1) + self.SetSel(charNo) + + def HookHandlers(self): # children can override, but should still call me! + # self.HookAllKeyStrokes(self.OnKey) + self.HookMessage(self.OnCheckExternalDocumentUpdated, MSG_CHECK_EXTERNAL_FILE) + self.HookMessage(self.OnRClick, win32con.WM_RBUTTONDOWN) + self.HookMessage(self.OnSetFocus, win32con.WM_SETFOCUS) + self.HookMessage(self.OnKeyDown, win32con.WM_KEYDOWN) + self.HookKeyStroke(self.OnKeyCtrlY, 25) # ^Y + self.HookKeyStroke(self.OnKeyCtrlG, 7) # ^G + self.HookKeyStroke(self.OnKeyTab, 9) # TAB + self.HookKeyStroke(self.OnKeyEnter, 13) # Enter + self.HookCommand(self.OnCmdLocateFile, ID_LOCATE_FILE) + self.HookCommand(self.OnCmdGotoLine, ID_GOTO_LINE) + self.HookCommand(self.OnEditPaste, afxres.ID_EDIT_PASTE) + self.HookCommand(self.OnEditCut, afxres.ID_EDIT_CUT) + + # Hook Handlers + def OnSetFocus(self, msg): + # Even though we use file change notifications, we should be very sure about it here. + self.OnCheckExternalDocumentUpdated(msg) + + def OnRClick(self, params): + menu = win32ui.CreatePopupMenu() + + # look for a module name + line = self._obj_.GetLine().strip() + flags = win32con.MF_STRING | win32con.MF_ENABLED + if patImport.match(line) == len(line): + menu.AppendMenu( + flags, ID_LOCATE_FILE, "&Locate %s.py" % patImport.group("name") + ) + menu.AppendMenu(win32con.MF_SEPARATOR) + menu.AppendMenu(flags, win32ui.ID_EDIT_UNDO, "&Undo") + menu.AppendMenu(win32con.MF_SEPARATOR) + menu.AppendMenu(flags, win32ui.ID_EDIT_CUT, "Cu&t") + menu.AppendMenu(flags, win32ui.ID_EDIT_COPY, "&Copy") + menu.AppendMenu(flags, win32ui.ID_EDIT_PASTE, "&Paste") + menu.AppendMenu(flags, win32con.MF_SEPARATOR) + menu.AppendMenu(flags, win32ui.ID_EDIT_SELECT_ALL, "&Select all") + menu.AppendMenu(flags, win32con.MF_SEPARATOR) + menu.AppendMenu(flags, ID_GOTO_LINE, "&Goto line...") + menu.TrackPopupMenu(params[5]) + return 0 + + def OnCmdGotoLine(self, cmd, code): + self.GotoLine() + return 0 + + def OnCmdLocateFile(self, cmd, code): + modName = patImport.group("name") + if not modName: + return 0 + import pywin.framework.scriptutils + + fileName = pywin.framework.scriptutils.LocatePythonFile(modName) + if fileName is None: + win32ui.SetStatusText("Can't locate module %s" % modName) + else: + win32ui.GetApp().OpenDocumentFile(fileName) + return 0 + + # Key handlers + def OnKeyEnter(self, key): + if not self.GetDocument().CheckMakeDocumentWritable(): + return 0 + curLine = self._obj_.GetLine() + self._obj_.ReplaceSel("\r\n") # insert the newline + # If the current line indicates the next should be indented, + # then copy the current indentation to this line. + res = patIndent.match(curLine, 0) + if res > 0 and curLine.strip(): + curIndent = patIndent.group(1) + self._obj_.ReplaceSel(curIndent) + return 0 # dont pass on + + def OnKeyCtrlY(self, key): + if not self.GetDocument().CheckMakeDocumentWritable(): + return 0 + self.CutCurLine() + return 0 # dont let him have it! + + def OnKeyCtrlG(self, key): + self.GotoLine() + return 0 # dont let him have it! + + def OnKeyTab(self, key): + if not self.GetDocument().CheckMakeDocumentWritable(): + return 0 + start, end = self._obj_.GetSel() + if start == end: # normal TAB key + self.Indent() + return 0 # we handled this. + + # Otherwise it is a block indent/dedent. + if start > end: + start, end = end, start # swap them. + startLine = self._obj_.LineFromChar(start) + endLine = self._obj_.LineFromChar(end) + + self.BlockDent(win32api.GetKeyState(win32con.VK_SHIFT) >= 0, startLine, endLine) + return 0 + + def OnEditPaste(self, id, code): + # Return 1 if we can make the file editable.(or it already is!) + return self.GetDocument().CheckMakeDocumentWritable() + + def OnEditCut(self, id, code): + # Return 1 if we can make the file editable.(or it already is!) + return self.GetDocument().CheckMakeDocumentWritable() + + def OnKeyDown(self, msg): + key = msg[2] + if win32api.GetKeyState(win32con.VK_CONTROL) & 0x8000: + modList = MODIFYING_VK_KEYS_CTRL + elif win32api.GetKeyState(win32con.VK_MENU) & 0x8000: + modList = MODIFYING_VK_KEYS_ALT + else: + modList = MODIFYING_VK_KEYS + + if key in modList: + # Return 1 if we can make the file editable.(or it already is!) + return self.GetDocument().CheckMakeDocumentWritable() + return 1 # Pass it on OK + + # def OnKey(self, key): + # return self.GetDocument().CheckMakeDocumentWritable() + + def OnCheckExternalDocumentUpdated(self, msg): + if self._obj_ is None or self.bCheckingFile: + return + self.bCheckingFile = 1 + self.GetDocument().CheckExternalDocumentUpdated() + self.bCheckingFile = 0 + + +from .template import EditorTemplateBase + + +class EditorTemplate(EditorTemplateBase): + def __init__( + self, res=win32ui.IDR_TEXTTYPE, makeDoc=None, makeFrame=None, makeView=None + ): + if makeDoc is None: + makeDoc = EditorDocument + if makeView is None: + makeView = EditorView + EditorTemplateBase.__init__(self, res, makeDoc, makeFrame, makeView) + + def _CreateDocTemplate(self, resourceId): + return win32ui.CreateRichEditDocTemplate(resourceId) + + def CreateWin32uiDocument(self): + return self.DoCreateRichEditDoc() + + +def Create(fileName=None, title=None, template=None): + return editorTemplate.OpenDocumentFile(fileName) + + +from pywin.framework.editor import GetDefaultEditorModuleName + +prefModule = GetDefaultEditorModuleName() +# Initialize only if this is the "default" editor. +if __name__ == prefModule: + # For debugging purposes, when this module may be reloaded many times. + try: + win32ui.GetApp().RemoveDocTemplate(editorTemplate) + except (NameError, win32ui.error): + pass + + editorTemplate = EditorTemplate() + win32ui.GetApp().AddDocTemplate(editorTemplate) diff --git a/myenv/Lib/site-packages/pythonwin/pywin/framework/editor/frame.py b/myenv/Lib/site-packages/pythonwin/pywin/framework/editor/frame.py new file mode 100644 index 000000000..e927d16e5 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/framework/editor/frame.py @@ -0,0 +1,74 @@ +# frame.py - The MDI frame window for an editor. +import pywin.framework.window +import win32con +import win32ui + +from . import ModuleBrowser + + +class EditorFrame(pywin.framework.window.MDIChildWnd): + def OnCreateClient(self, cp, context): + # Create the default view as specified by the template (ie, the editor view) + view = context.template.MakeView(context.doc) + # Create the browser view. + browserView = ModuleBrowser.BrowserView(context.doc) + view2 = context.template.MakeView(context.doc) + + splitter = win32ui.CreateSplitter() + style = win32con.WS_CHILD | win32con.WS_VISIBLE + splitter.CreateStatic(self, 1, 2, style, win32ui.AFX_IDW_PANE_FIRST) + sub_splitter = self.sub_splitter = win32ui.CreateSplitter() + sub_splitter.CreateStatic(splitter, 2, 1, style, win32ui.AFX_IDW_PANE_FIRST + 1) + + # Note we must add the default view first, so that doc.GetFirstView() returns the editor view. + sub_splitter.CreateView(view, 1, 0, (0, 0)) + splitter.CreateView(browserView, 0, 0, (0, 0)) + sub_splitter.CreateView(view2, 0, 0, (0, 0)) + + ## print "First view is", context.doc.GetFirstView() + ## print "Views are", view, view2, browserView + ## print "Parents are", view.GetParent(), view2.GetParent(), browserView.GetParent() + ## print "Splitter is", splitter + ## print "sub splitter is", sub_splitter + ## Old + ## splitter.CreateStatic (self, 1, 2) + ## splitter.CreateView(view, 0, 1, (0,0)) # size ignored. + ## splitter.CreateView (browserView, 0, 0, (0, 0)) + + # Restrict the size of the browser splitter (and we can avoid filling + # it until it is shown) + splitter.SetColumnInfo(0, 10, 20) + # And the active view is our default view (so it gets initial focus) + self.SetActiveView(view) + + def GetEditorView(self): + # In a multi-view (eg, splitter) environment, get + # an editor (ie, scintilla) view + # Look for the splitter opened the most! + if self.sub_splitter is None: + return self.GetDlgItem(win32ui.AFX_IDW_PANE_FIRST) + v1 = self.sub_splitter.GetPane(0, 0) + v2 = self.sub_splitter.GetPane(1, 0) + r1 = v1.GetWindowRect() + r2 = v2.GetWindowRect() + if r1[3] - r1[1] > r2[3] - r2[1]: + return v1 + return v2 + + def GetBrowserView(self): + # XXX - should fix this :-) + return self.GetActiveDocument().GetAllViews()[1] + + def OnClose(self): + doc = self.GetActiveDocument() + if not doc.SaveModified(): + ## Cancel button selected from Save dialog, do not actually close + ## print 'close cancelled' + return 0 + ## So the 'Save' dialog doesn't come up twice + doc._obj_.SetModifiedFlag(False) + + # Must force the module browser to close itself here (OnDestroy for the view itself is too late!) + self.sub_splitter = None # ensure no circles! + self.GetBrowserView().DestroyBrowser() + return self._obj_.OnClose() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/framework/editor/template.py b/myenv/Lib/site-packages/pythonwin/pywin/framework/editor/template.py new file mode 100644 index 000000000..362a74a62 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/framework/editor/template.py @@ -0,0 +1,60 @@ +import os + +import pywin.framework.window +import win32api +import win32ui +from pywin.mfc import docview + +from . import frame + +ParentEditorTemplate = docview.DocTemplate + + +class EditorTemplateBase(ParentEditorTemplate): + def __init__( + self, res=win32ui.IDR_TEXTTYPE, makeDoc=None, makeFrame=None, makeView=None + ): + if makeFrame is None: + makeFrame = frame.EditorFrame + ParentEditorTemplate.__init__(self, res, makeDoc, makeFrame, makeView) + + def _CreateDocTemplate(self, resourceId): + assert 0, "You must override this" + + def CreateWin32uiDocument(self): + assert 0, "You must override this" + + def GetFileExtensions(self): + return ".txt", ".py" + + def MatchDocType(self, fileName, fileType): + doc = self.FindOpenDocument(fileName) + if doc: + return doc + ext = os.path.splitext(fileName)[1].lower() + if ext in self.GetFileExtensions(): + return win32ui.CDocTemplate_Confidence_yesAttemptNative + return win32ui.CDocTemplate_Confidence_maybeAttemptForeign + + def InitialUpdateFrame(self, frame, doc, makeVisible=1): + self._obj_.InitialUpdateFrame(frame, doc, makeVisible) # call default handler. + doc._UpdateUIForState() + + def GetPythonPropertyPages(self): + """Returns a list of property pages""" + from . import configui + + return [configui.EditorPropertyPage(), configui.EditorWhitespacePropertyPage()] + + def OpenDocumentFile(self, filename, bMakeVisible=1): + if filename is not None: + try: + path = os.path.split(filename)[0] + # print "The editor is translating", `filename`,"to", + filename = win32api.FindFiles(filename)[0][8] + filename = os.path.join(path, filename) + # print `filename` + except (win32api.error, IndexError) as details: + pass + # print "Couldnt get the full filename!", details + return self._obj_.OpenDocumentFile(filename, bMakeVisible) diff --git a/myenv/Lib/site-packages/pythonwin/pywin/framework/editor/vss.py b/myenv/Lib/site-packages/pythonwin/pywin/framework/editor/vss.py new file mode 100644 index 000000000..718f83eea --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/framework/editor/vss.py @@ -0,0 +1,104 @@ +# vss.py -- Source Control using Microsoft VSS. + +# Provides routines for checking files out of VSS. +# +# Uses an INI file very similar to how VB integrates with VSS - even +# as far as using the same name. + +# The file must be named "Mssccprj.scc", and be in the format of +# an INI file. This file may be in a parent directory, in which +# case the project name will be built from what is specified in the +# ini file, plus the path from the INI file to the file itself. +# +# The INI file should have a [Python] section, and a +# Project=Project Name +# and optionally +# Database=?? + + +import os +import sys +import traceback + +import win32api +import win32ui + +g_iniName = "Mssccprj.scc" # Use the same INI name as VB! + +g_sourceSafe = None + + +def FindVssProjectInfo(fullfname): + """Looks up the file system for an INI file describing the project. + + Looking up the tree is for ni style packages. + + Returns (projectName, pathToFileName) where pathToFileName contains + the path from the ini file to the actual file. + """ + path, fnameonly = os.path.split(fullfname) + origPath = path + project = "" + retPaths = [fnameonly] + while not project: + iniName = os.path.join(path, g_iniName) + database = win32api.GetProfileVal("Python", "Database", "", iniName) + project = win32api.GetProfileVal("Python", "Project", "", iniName) + if project: + break + # No valid INI file in this directory - look up a level. + path, addpath = os.path.split(path) + if not addpath: # Root? + break + retPaths.insert(0, addpath) + if not project: + win32ui.MessageBox( + "%s\r\n\r\nThis directory is not configured for Python/VSS" % origPath + ) + return + return project, "/".join(retPaths), database + + +def CheckoutFile(fileName): + global g_sourceSafe + import pythoncom + + ok = 0 + # Assumes the fileName has a complete path, + # and that the INI file can be found in that path + # (or a parent path if a ni style package) + try: + import win32com.client + import win32com.client.gencache + + mod = win32com.client.gencache.EnsureModule( + "{783CD4E0-9D54-11CF-B8EE-00608CC9A71F}", 0, 5, 0 + ) + if mod is None: + win32ui.MessageBox( + "VSS does not appear to be installed. The TypeInfo can not be created" + ) + return ok + + rc = FindVssProjectInfo(fileName) + if rc is None: + return + project, vssFname, database = rc + if g_sourceSafe is None: + g_sourceSafe = win32com.client.Dispatch("SourceSafe") + # SS seems a bit wierd. It defaults the arguments as empty strings, but + # then complains when they are used - so we pass "Missing" + if not database: + database = pythoncom.Missing + g_sourceSafe.Open(database, pythoncom.Missing, pythoncom.Missing) + item = g_sourceSafe.VSSItem("$/%s/%s" % (project, vssFname)) + item.Checkout(None, fileName) + ok = 1 + except pythoncom.com_error as exc: + win32ui.MessageBox(exc.strerror, "Error checking out file") + except: + typ, val, tb = sys.exc_info() + traceback.print_exc() + win32ui.MessageBox("%s - %s" % (str(typ), str(val)), "Error checking out file") + tb = None # Cleanup a cycle + return ok diff --git a/myenv/Lib/site-packages/pythonwin/pywin/framework/help.py b/myenv/Lib/site-packages/pythonwin/pywin/framework/help.py new file mode 100644 index 000000000..ab664b4d1 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/framework/help.py @@ -0,0 +1,173 @@ +# help.py - help utilities for PythonWin. +import os + +import regutil +import win32api +import win32con +import win32ui + +htmlhelp_handle = None + +html_help_command_translators = { + win32con.HELP_CONTENTS: 1, # HH_DISPLAY_TOC + win32con.HELP_CONTEXT: 15, # HH_HELP_CONTEXT + win32con.HELP_FINDER: 1, # HH_DISPLAY_TOC +} + + +def FinalizeHelp(): + global htmlhelp_handle + if htmlhelp_handle is not None: + import win32help + + try: + # frame = win32ui.GetMainFrame().GetSafeHwnd() + frame = 0 + win32help.HtmlHelp(frame, None, win32help.HH_UNINITIALIZE, htmlhelp_handle) + except win32help.error: + print("Failed to finalize htmlhelp!") + htmlhelp_handle = None + + +def OpenHelpFile(fileName, helpCmd=None, helpArg=None): + "Open a help file, given a full path" + # default help arg. + win32ui.DoWaitCursor(1) + try: + if helpCmd is None: + helpCmd = win32con.HELP_CONTENTS + ext = os.path.splitext(fileName)[1].lower() + if ext == ".hlp": + win32api.WinHelp( + win32ui.GetMainFrame().GetSafeHwnd(), fileName, helpCmd, helpArg + ) + # XXX - using the htmlhelp API wreaks havoc with keyboard shortcuts + # so we disable it, forcing ShellExecute, which works fine (but + # doesn't close the help file when Pythonwin is closed. + # Tom Heller also points out http://www.microsoft.com/mind/0499/faq/faq0499.asp, + # which may or may not be related. + elif 0 and ext == ".chm": + import win32help + + global htmlhelp_handle + helpCmd = html_help_command_translators.get(helpCmd, helpCmd) + # frame = win32ui.GetMainFrame().GetSafeHwnd() + frame = 0 # Dont want it overlapping ours! + if htmlhelp_handle is None: + htmlhelp_hwnd, htmlhelp_handle = win32help.HtmlHelp( + frame, None, win32help.HH_INITIALIZE + ) + win32help.HtmlHelp(frame, fileName, helpCmd, helpArg) + else: + # Hope that the extension is registered, and we know what to do! + win32api.ShellExecute(0, "open", fileName, None, "", win32con.SW_SHOW) + return fileName + finally: + win32ui.DoWaitCursor(-1) + + +def ListAllHelpFiles(): + ret = [] + ret = _ListAllHelpFilesInRoot(win32con.HKEY_LOCAL_MACHINE) + # Ensure we don't get dups. + for item in _ListAllHelpFilesInRoot(win32con.HKEY_CURRENT_USER): + if item not in ret: + ret.append(item) + return ret + + +def _ListAllHelpFilesInRoot(root): + """Returns a list of (helpDesc, helpFname) for all registered help files""" + import regutil + + retList = [] + try: + key = win32api.RegOpenKey( + root, regutil.BuildDefaultPythonKey() + "\\Help", 0, win32con.KEY_READ + ) + except win32api.error as exc: + import winerror + + if exc.winerror != winerror.ERROR_FILE_NOT_FOUND: + raise + return retList + try: + keyNo = 0 + while 1: + try: + helpDesc = win32api.RegEnumKey(key, keyNo) + helpFile = win32api.RegQueryValue(key, helpDesc) + retList.append((helpDesc, helpFile)) + keyNo = keyNo + 1 + except win32api.error as exc: + import winerror + + if exc.winerror != winerror.ERROR_NO_MORE_ITEMS: + raise + break + finally: + win32api.RegCloseKey(key) + return retList + + +def SelectAndRunHelpFile(): + from pywin.dialogs import list + + helpFiles = ListAllHelpFiles() + if len(helpFiles) == 1: + # only 1 help file registered - probably ours - no point asking + index = 0 + else: + index = list.SelectFromLists("Select Help file", helpFiles, ["Title"]) + if index is not None: + OpenHelpFile(helpFiles[index][1]) + + +helpIDMap = None + + +def SetHelpMenuOtherHelp(mainMenu): + """Modifies the main Help Menu to handle all registered help files. + mainMenu -- The main menu to modify - usually from docTemplate.GetSharedMenu() + """ + + # Load all help files from the registry. + global helpIDMap + if helpIDMap is None: + helpIDMap = {} + cmdID = win32ui.ID_HELP_OTHER + excludeList = ["Main Python Documentation", "Pythonwin Reference"] + firstList = ListAllHelpFiles() + # We actually want to not only exclude these entries, but + # their help file names (as many entries may share the same name) + excludeFnames = [] + for desc, fname in firstList: + if desc in excludeList: + excludeFnames.append(fname) + + helpDescs = [] + for desc, fname in firstList: + if fname not in excludeFnames: + helpIDMap[cmdID] = (desc, fname) + win32ui.GetMainFrame().HookCommand(HandleHelpOtherCommand, cmdID) + cmdID = cmdID + 1 + + helpMenu = mainMenu.GetSubMenu( + mainMenu.GetMenuItemCount() - 1 + ) # Help menu always last. + otherHelpMenuPos = 2 # cant search for ID, as sub-menu has no ID. + otherMenu = helpMenu.GetSubMenu(otherHelpMenuPos) + while otherMenu.GetMenuItemCount(): + otherMenu.DeleteMenu(0, win32con.MF_BYPOSITION) + + if helpIDMap: + for id, (desc, fname) in helpIDMap.items(): + otherMenu.AppendMenu(win32con.MF_ENABLED | win32con.MF_STRING, id, desc) + else: + helpMenu.EnableMenuItem( + otherHelpMenuPos, win32con.MF_BYPOSITION | win32con.MF_GRAYED + ) + + +def HandleHelpOtherCommand(cmd, code): + OpenHelpFile(helpIDMap[cmd][1]) diff --git a/myenv/Lib/site-packages/pythonwin/pywin/framework/interact.py b/myenv/Lib/site-packages/pythonwin/pywin/framework/interact.py new file mode 100644 index 000000000..5f0b87f2e --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/framework/interact.py @@ -0,0 +1,985 @@ +################################################################## +## +## Interactive Shell Window +## + +import array +import code +import os +import string +import sys +import traceback + +import __main__ +import afxres +import pywin.framework.app +import pywin.scintilla.control +import pywin.scintilla.formatter +import pywin.scintilla.IDLEenvironment +import win32api +import win32clipboard +import win32con +import win32ui + +## sequential after ID_GOTO_LINE defined in editor.py +ID_EDIT_COPY_CODE = 0xE2002 +ID_EDIT_EXEC_CLIPBOARD = 0x2003 + +trace = pywin.scintilla.formatter.trace + +import re + +from . import winout + +# from IDLE. +_is_block_opener = re.compile(r":\s*(#.*)?$").search +_is_block_closer = re.compile( + r""" + \s* + ( return + | break + | continue + | raise + | pass + ) + \b +""", + re.VERBOSE, +).match + +tracebackHeader = "Traceback (".encode("ascii") + +sectionProfile = "Interactive Window" +valueFormatTitle = "FormatTitle" +valueFormatInput = "FormatInput" +valueFormatOutput = "FormatOutput" +valueFormatOutputError = "FormatOutputError" + +# These are defaults only. Values are read from the registry. +formatTitle = (-536870897, 0, 220, 0, 16711680, 184, 34, "Arial") +formatInput = (-402653169, 0, 200, 0, 0, 0, 49, "Courier New") +formatOutput = (-402653169, 0, 200, 0, 8421376, 0, 49, "Courier New") +formatOutputError = (-402653169, 0, 200, 0, 255, 0, 49, "Courier New") + +try: + sys.ps1 +except AttributeError: + sys.ps1 = ">>> " + sys.ps2 = "... " + + +def LoadPreference(preference, default=""): + return win32ui.GetProfileVal(sectionProfile, preference, default) + + +def SavePreference(prefName, prefValue): + win32ui.WriteProfileVal(sectionProfile, prefName, prefValue) + + +def GetPromptPrefix(line): + ps1 = sys.ps1 + if line[: len(ps1)] == ps1: + return ps1 + ps2 = sys.ps2 + if line[: len(ps2)] == ps2: + return ps2 + + +############################################################# +# +# Colorizer related code. +# +############################################################# +STYLE_INTERACTIVE_EOL = "Interactive EOL" +STYLE_INTERACTIVE_OUTPUT = "Interactive Output" +STYLE_INTERACTIVE_PROMPT = "Interactive Prompt" +STYLE_INTERACTIVE_BANNER = "Interactive Banner" +STYLE_INTERACTIVE_ERROR = "Interactive Error" +STYLE_INTERACTIVE_ERROR_FINALLINE = "Interactive Error (final line)" + +INTERACTIVE_STYLES = [ + STYLE_INTERACTIVE_EOL, + STYLE_INTERACTIVE_OUTPUT, + STYLE_INTERACTIVE_PROMPT, + STYLE_INTERACTIVE_BANNER, + STYLE_INTERACTIVE_ERROR, + STYLE_INTERACTIVE_ERROR_FINALLINE, +] + +FormatterParent = pywin.scintilla.formatter.PythonSourceFormatter + + +class InteractiveFormatter(FormatterParent): + def __init__(self, scintilla): + FormatterParent.__init__(self, scintilla) + self.bannerDisplayed = False + + def SetStyles(self): + FormatterParent.SetStyles(self) + Style = pywin.scintilla.formatter.Style + self.RegisterStyle(Style(STYLE_INTERACTIVE_EOL, STYLE_INTERACTIVE_PROMPT)) + self.RegisterStyle(Style(STYLE_INTERACTIVE_PROMPT, formatInput)) + self.RegisterStyle(Style(STYLE_INTERACTIVE_OUTPUT, formatOutput)) + self.RegisterStyle(Style(STYLE_INTERACTIVE_BANNER, formatTitle)) + self.RegisterStyle(Style(STYLE_INTERACTIVE_ERROR, formatOutputError)) + self.RegisterStyle( + Style(STYLE_INTERACTIVE_ERROR_FINALLINE, STYLE_INTERACTIVE_ERROR) + ) + + def LoadPreference(self, name, default): + rc = win32ui.GetProfileVal("Format", name, default) + if rc == default: + rc = win32ui.GetProfileVal(sectionProfile, name, default) + return rc + + def ColorizeInteractiveCode(self, cdoc, styleStart, stylePyStart): + lengthDoc = len(cdoc) + if lengthDoc == 0: + return + state = styleStart + # As per comments in Colorize(), we work with the raw utf8 + # bytes. To avoid too muych py3k pain, we treat each utf8 byte + # as a latin-1 unicode character - we only use it to compare + # against ascii chars anyway... + chNext = cdoc[0:1].decode("latin-1") + startSeg = 0 + i = 0 + lastState = state # debug only + while i < lengthDoc: + ch = chNext + chNext = cdoc[i + 1 : i + 2].decode("latin-1") + + # trace("ch=%r, i=%d, next=%r, state=%s" % (ch, i, chNext, state)) + if state == STYLE_INTERACTIVE_EOL: + if ch not in "\r\n": + self.ColorSeg(startSeg, i - 1, state) + startSeg = i + if ch in (sys.ps1[0], sys.ps2[0]): + state = STYLE_INTERACTIVE_PROMPT + elif cdoc[i : i + len(tracebackHeader)] == tracebackHeader: + state = STYLE_INTERACTIVE_ERROR + else: + state = STYLE_INTERACTIVE_OUTPUT + elif state == STYLE_INTERACTIVE_PROMPT: + if ch not in sys.ps1 + sys.ps2 + " ": + self.ColorSeg(startSeg, i - 1, state) + startSeg = i + if ch in "\r\n": + state = STYLE_INTERACTIVE_EOL + else: + state = stylePyStart # Start coloring Python code. + elif state in (STYLE_INTERACTIVE_OUTPUT,): + if ch in "\r\n": + self.ColorSeg(startSeg, i - 1, state) + startSeg = i + state = STYLE_INTERACTIVE_EOL + elif state == STYLE_INTERACTIVE_ERROR: + if ch in "\r\n" and chNext and chNext not in string.whitespace: + # Everything including me + self.ColorSeg(startSeg, i, state) + startSeg = i + 1 + state = STYLE_INTERACTIVE_ERROR_FINALLINE + elif i == 0 and ch not in string.whitespace: + # If we are coloring from the start of a line, + # we need this better check for the last line + # Color up to not including me + self.ColorSeg(startSeg, i - 1, state) + startSeg = i + state = STYLE_INTERACTIVE_ERROR_FINALLINE + elif state == STYLE_INTERACTIVE_ERROR_FINALLINE: + if ch in "\r\n": + self.ColorSeg(startSeg, i - 1, state) + startSeg = i + state = STYLE_INTERACTIVE_EOL + elif state == STYLE_INTERACTIVE_BANNER: + if ch in "\r\n" and (chNext == "" or chNext in ">["): + # Everything including me + self.ColorSeg(startSeg, i - 1, state) + startSeg = i + state = STYLE_INTERACTIVE_EOL + else: + # It is a PythonColorizer state - seek past the end of the line + # and ask the Python colorizer to color that. + end = startSeg + while end < lengthDoc and cdoc[end] not in "\r\n".encode("ascii"): + end = end + 1 + self.ColorizePythonCode(cdoc[:end], startSeg, state) + stylePyStart = self.GetStringStyle(end - 1) + if stylePyStart is None: + stylePyStart = pywin.scintilla.formatter.STYLE_DEFAULT + else: + stylePyStart = stylePyStart.name + startSeg = end + i = end - 1 # ready for increment. + chNext = cdoc[end : end + 1].decode("latin-1") + state = STYLE_INTERACTIVE_EOL + if lastState != state: + lastState = state + i = i + 1 + # and the rest + if startSeg < i: + self.ColorSeg(startSeg, i - 1, state) + + def Colorize(self, start=0, end=-1): + # scintilla's formatting is all done in terms of utf, so + # we work with utf8 bytes instead of unicode. This magically + # works as any extended chars found in the utf8 don't change + # the semantics. + stringVal = self.scintilla.GetTextRange(start, end, decode=False) + styleStart = None + stylePyStart = None + if start > 1: + # Likely we are being asked to color from the start of the line. + # We find the last formatted character on the previous line. + # If TQString, we continue it. Otherwise, we reset. + look = start - 1 + while look and self.scintilla.SCIGetCharAt(look) in "\n\r": + look = look - 1 + if look and look < start - 1: # Did we find a char before the \n\r sets? + strstyle = self.GetStringStyle(look) + quote_char = None + if strstyle is not None: + if strstyle.name == pywin.scintilla.formatter.STYLE_TQSSTRING: + quote_char = "'" + elif strstyle.name == pywin.scintilla.formatter.STYLE_TQDSTRING: + quote_char = '"' + if quote_char is not None: + # It is a TQS. If the TQS is not terminated, we + # carry the style through. + if look > 2: + look_str = ( + self.scintilla.SCIGetCharAt(look - 2) + + self.scintilla.SCIGetCharAt(look - 1) + + self.scintilla.SCIGetCharAt(look) + ) + if look_str != quote_char * 3: + stylePyStart = strstyle.name + if stylePyStart is None: + stylePyStart = pywin.scintilla.formatter.STYLE_DEFAULT + + if start > 0: + stylenum = self.scintilla.SCIGetStyleAt(start - 1) + styleStart = self.GetStyleByNum(stylenum).name + elif self.bannerDisplayed: + styleStart = STYLE_INTERACTIVE_EOL + else: + styleStart = STYLE_INTERACTIVE_BANNER + self.bannerDisplayed = True + self.scintilla.SCIStartStyling(start, 31) + self.style_buffer = array.array("b", (0,) * len(stringVal)) + self.ColorizeInteractiveCode(stringVal, styleStart, stylePyStart) + self.scintilla.SCISetStylingEx(self.style_buffer) + self.style_buffer = None + + +############################################################### +# +# This class handles the Python interactive interpreter. +# +# It uses a basic EditWindow, and does all the magic. +# This is triggered by the enter key hander attached by the +# start-up code. It determines if a command is to be executed +# or continued (ie, emit "... ") by snooping around the current +# line, looking for the prompts +# +class PythonwinInteractiveInterpreter(code.InteractiveInterpreter): + def __init__(self, locals=None, globals=None): + if locals is None: + locals = __main__.__dict__ + if globals is None: + globals = locals + self.globals = globals + code.InteractiveInterpreter.__init__(self, locals) + + def showsyntaxerror(self, filename=None): + sys.stderr.write( + tracebackHeader.decode("ascii") + ) # So the color syntaxer recognises it. + code.InteractiveInterpreter.showsyntaxerror(self, filename) + + def runcode(self, code): + try: + exec(code, self.globals, self.locals) + except SystemExit: + raise + except: + self.showtraceback() + + +class InteractiveCore: + def __init__(self, banner=None): + self.banner = banner + + # LoadFontPreferences() + def Init(self): + self.oldStdOut = self.oldStdErr = None + + # self.SetWordWrap(win32ui.CRichEditView_WrapNone) + self.interp = PythonwinInteractiveInterpreter() + + self.OutputGrab() # Release at cleanup. + + if self.GetTextLength() == 0: + if self.banner is None: + suffix = "" + if win32ui.debug: + suffix = ", debug build" + sys.stderr.write( + "PythonWin %s on %s%s.\n" % (sys.version, sys.platform, suffix) + ) + sys.stderr.write( + "Portions %s - see 'Help/About PythonWin' for further copyright information.\n" + % (win32ui.copyright,) + ) + else: + sys.stderr.write(banner) + rcfile = os.environ.get("PYTHONSTARTUP") + if rcfile: + import __main__ + + try: + exec( + compile( + open(rcfile, "rb").read(), rcfile, "exec", dont_inherit=True + ), + __main__.__dict__, + __main__.__dict__, + ) + except: + sys.stderr.write( + ">>> \nError executing PYTHONSTARTUP script %r\n" % (rcfile) + ) + traceback.print_exc(file=sys.stderr) + self.AppendToPrompt([]) + + def SetContext(self, globals, locals, name="Dbg"): + oldPrompt = sys.ps1 + if globals is None: + # Reset + sys.ps1 = ">>> " + sys.ps2 = "... " + locals = globals = __main__.__dict__ + else: + sys.ps1 = "[%s]>>> " % name + sys.ps2 = "[%s]... " % name + self.interp.locals = locals + self.interp.globals = globals + self.AppendToPrompt([], oldPrompt) + + def GetContext(self): + return self.interp.globals, self.interp.locals + + def DoGetLine(self, line=-1): + if line == -1: + line = self.LineFromChar() + line = self.GetLine(line) + while line and line[-1] in ("\r", "\n"): + line = line[:-1] + return line + + def AppendToPrompt(self, bufLines, oldPrompt=None): + "Take a command and stick it at the end of the buffer (with python prompts inserted if required)." + self.flush() + lastLineNo = self.GetLineCount() - 1 + line = self.DoGetLine(lastLineNo) + if oldPrompt and line == oldPrompt: + self.SetSel(self.GetTextLength() - len(oldPrompt), self.GetTextLength()) + self.ReplaceSel(sys.ps1) + elif line != str(sys.ps1): + if len(line) != 0: + self.write("\n") + self.write(sys.ps1) + self.flush() + self.idle.text.mark_set("iomark", "end-1c") + if not bufLines: + return + terms = (["\n" + sys.ps2] * (len(bufLines) - 1)) + [""] + for bufLine, term in zip(bufLines, terms): + if bufLine.strip(): + self.write(bufLine + term) + self.flush() + + def EnsureNoPrompt(self): + # Get ready to write some text NOT at a Python prompt. + self.flush() + lastLineNo = self.GetLineCount() - 1 + line = self.DoGetLine(lastLineNo) + if not line or line in (sys.ps1, sys.ps2): + self.SetSel(self.GetTextLength() - len(line), self.GetTextLength()) + self.ReplaceSel("") + else: + # Just add a new line. + self.write("\n") + + def _GetSubConfigNames(self): + return ["interactive"] # Allow [Keys:Interactive] sections to be specific + + def HookHandlers(self): + # Hook menu command (executed when a menu item with that ID is selected from a menu/toolbar + self.HookCommand(self.OnSelectBlock, win32ui.ID_EDIT_SELECT_BLOCK) + self.HookCommand(self.OnEditCopyCode, ID_EDIT_COPY_CODE) + self.HookCommand(self.OnEditExecClipboard, ID_EDIT_EXEC_CLIPBOARD) + mod = pywin.scintilla.IDLEenvironment.GetIDLEModule("IdleHistory") + if mod is not None: + self.history = mod.History(self.idle.text, "\n" + sys.ps2) + else: + self.history = None + # hack for now for event handling. + + # GetBlockBoundary takes a line number, and will return the + # start and and line numbers of the block, and a flag indicating if the + # block is a Python code block. + # If the line specified has a Python prompt, then the lines are parsed + # backwards and forwards, and the flag is true. + # If the line does not start with a prompt, the block is searched forward + # and backward until a prompt _is_ found, and all lines in between without + # prompts are returned, and the flag is false. + def GetBlockBoundary(self, lineNo): + line = self.DoGetLine(lineNo) + maxLineNo = self.GetLineCount() - 1 + prefix = GetPromptPrefix(line) + if prefix is None: # Non code block + flag = 0 + startLineNo = lineNo + while startLineNo > 0: + if GetPromptPrefix(self.DoGetLine(startLineNo - 1)) is not None: + break # there _is_ a prompt + startLineNo = startLineNo - 1 + endLineNo = lineNo + while endLineNo < maxLineNo: + if GetPromptPrefix(self.DoGetLine(endLineNo + 1)) is not None: + break # there _is_ a prompt + endLineNo = endLineNo + 1 + else: # Code block + flag = 1 + startLineNo = lineNo + while startLineNo > 0 and prefix != str(sys.ps1): + prefix = GetPromptPrefix(self.DoGetLine(startLineNo - 1)) + if prefix is None: + break + # there is no prompt. + startLineNo = startLineNo - 1 + endLineNo = lineNo + while endLineNo < maxLineNo: + prefix = GetPromptPrefix(self.DoGetLine(endLineNo + 1)) + if prefix is None: + break # there is no prompt + if prefix == str(sys.ps1): + break # this is another command + endLineNo = endLineNo + 1 + # continue until end of buffer, or no prompt + return (startLineNo, endLineNo, flag) + + def ExtractCommand(self, lines): + start, end = lines + retList = [] + while end >= start: + thisLine = self.DoGetLine(end) + promptLen = len(GetPromptPrefix(thisLine)) + retList = [thisLine[promptLen:]] + retList + end = end - 1 + return retList + + def OutputGrab(self): + # import win32traceutil; return + self.oldStdOut = sys.stdout + self.oldStdErr = sys.stderr + sys.stdout = self + sys.stderr = self + self.flush() + + def OutputRelease(self): + # a command may have overwritten these - only restore if not. + if self.oldStdOut is not None: + if sys.stdout == self: + sys.stdout = self.oldStdOut + if self.oldStdErr is not None: + if sys.stderr == self: + sys.stderr = self.oldStdErr + self.oldStdOut = None + self.oldStdErr = None + self.flush() + + ################################### + # + # Message/Command/Key Hooks. + # + # Enter key handler + # + def ProcessEnterEvent(self, event): + # If autocompletion has been triggered, complete and do not process event + if self.SCIAutoCActive(): + self.SCIAutoCComplete() + self.SCICancel() + return + + self.SCICancel() + # First, check for an error message + haveGrabbedOutput = 0 + if self.HandleSpecialLine(): + return 0 + + lineNo = self.LineFromChar() + start, end, isCode = self.GetBlockBoundary(lineNo) + # If we are not in a code block just go to the prompt (or create a new one) + if not isCode: + self.AppendToPrompt([]) + win32ui.SetStatusText(win32ui.LoadString(afxres.AFX_IDS_IDLEMESSAGE)) + return + + lines = self.ExtractCommand((start, end)) + + # If we are in a code-block, but it isnt at the end of the buffer + # then copy it to the end ready for editing and subsequent execution + if end != self.GetLineCount() - 1: + win32ui.SetStatusText("Press ENTER to execute command") + self.AppendToPrompt(lines) + self.SetSel(-2) + return + + # If SHIFT held down, we want new code here and now! + bNeedIndent = ( + win32api.GetKeyState(win32con.VK_SHIFT) < 0 + or win32api.GetKeyState(win32con.VK_CONTROL) < 0 + ) + if bNeedIndent: + self.ReplaceSel("\n") + else: + self.SetSel(-2) + self.ReplaceSel("\n") + source = "\n".join(lines) + while source and source[-1] in "\t ": + source = source[:-1] + self.OutputGrab() # grab the output for the command exec. + try: + if self.interp.runsource( + source, "" + ): # Need more input! + bNeedIndent = 1 + else: + # If the last line isnt empty, append a newline + if self.history is not None: + self.history.history_store(source) + self.AppendToPrompt([]) + win32ui.SetStatusText( + win32ui.LoadString(afxres.AFX_IDS_IDLEMESSAGE) + ) + # win32ui.SetStatusText('Successfully executed statement') + finally: + self.OutputRelease() + if bNeedIndent: + win32ui.SetStatusText("Ready to continue the command") + # Now attempt correct indentation (should use IDLE?) + curLine = self.DoGetLine(lineNo)[len(sys.ps2) :] + pos = 0 + indent = "" + while len(curLine) > pos and curLine[pos] in string.whitespace: + indent = indent + curLine[pos] + pos = pos + 1 + if _is_block_opener(curLine): + indent = indent + "\t" + elif _is_block_closer(curLine): + indent = indent[:-1] + # use ReplaceSel to ensure it goes at the cursor rather than end of buffer. + self.ReplaceSel(sys.ps2 + indent) + return 0 + + # ESC key handler + def ProcessEscEvent(self, event): + # Implement a cancel. + if self.SCIAutoCActive() or self.SCICallTipActive(): + self.SCICancel() + else: + win32ui.SetStatusText("Cancelled.") + self.AppendToPrompt(("",)) + return 0 + + def OnSelectBlock(self, command, code): + lineNo = self.LineFromChar() + start, end, isCode = self.GetBlockBoundary(lineNo) + startIndex = self.LineIndex(start) + endIndex = self.LineIndex(end + 1) - 2 # skip \r + \n + if endIndex < 0: # must be beyond end of buffer + endIndex = -2 # self.Length() + self.SetSel(startIndex, endIndex) + + def OnEditCopyCode(self, command, code): + """Sanitizes code from interactive window, removing prompts and output, + and inserts it in the clipboard.""" + code = self.GetSelText() + lines = code.splitlines() + out_lines = [] + for line in lines: + if line.startswith(sys.ps1): + line = line[len(sys.ps1) :] + out_lines.append(line) + elif line.startswith(sys.ps2): + line = line[len(sys.ps2) :] + out_lines.append(line) + out_code = os.linesep.join(out_lines) + win32clipboard.OpenClipboard() + try: + win32clipboard.SetClipboardData( + win32clipboard.CF_UNICODETEXT, str(out_code) + ) + finally: + win32clipboard.CloseClipboard() + + def OnEditExecClipboard(self, command, code): + """Executes python code directly from the clipboard.""" + win32clipboard.OpenClipboard() + try: + code = win32clipboard.GetClipboardData(win32clipboard.CF_UNICODETEXT) + finally: + win32clipboard.CloseClipboard() + + code = code.replace("\r\n", "\n") + "\n" + try: + o = compile(code, "", "exec") + exec(o, __main__.__dict__) + except: + traceback.print_exc() + + def GetRightMenuItems(self): + # Just override parents + ret = [] + flags = 0 + ret.append((flags, win32ui.ID_EDIT_UNDO, "&Undo")) + ret.append(win32con.MF_SEPARATOR) + ret.append((flags, win32ui.ID_EDIT_CUT, "Cu&t")) + ret.append((flags, win32ui.ID_EDIT_COPY, "&Copy")) + + start, end = self.GetSel() + if start != end: + ret.append((flags, ID_EDIT_COPY_CODE, "Copy code without prompts")) + if win32clipboard.IsClipboardFormatAvailable(win32clipboard.CF_UNICODETEXT): + ret.append( + (flags, ID_EDIT_EXEC_CLIPBOARD, "Execute python code from clipboard") + ) + + ret.append((flags, win32ui.ID_EDIT_PASTE, "&Paste")) + ret.append(win32con.MF_SEPARATOR) + ret.append((flags, win32ui.ID_EDIT_SELECT_ALL, "&Select all")) + ret.append((flags, win32ui.ID_EDIT_SELECT_BLOCK, "Select &block")) + ret.append((flags, win32ui.ID_VIEW_WHITESPACE, "View &Whitespace")) + return ret + + def MDINextEvent(self, event): + win32ui.GetMainFrame().MDINext(0) + + def MDIPrevEvent(self, event): + win32ui.GetMainFrame().MDINext(0) + + def WindowBackEvent(self, event): + parent = self.GetParentFrame() + if parent == win32ui.GetMainFrame(): + # It is docked. + try: + wnd, isactive = parent.MDIGetActive() + wnd.SetFocus() + except win32ui.error: + # No MDI window active! + pass + else: + # Normal Window + try: + lastActive = self.GetParentFrame().lastActive + # If the window is invalid, reset it. + if lastActive is not None and ( + lastActive._obj_ is None or lastActive.GetSafeHwnd() == 0 + ): + lastActive = self.GetParentFrame().lastActive = None + win32ui.SetStatusText("The last active Window has been closed.") + except AttributeError: + print("Can't find the last active window!") + lastActive = None + if lastActive is not None: + lastActive.MDIActivate() + + +class InteractiveView(InteractiveCore, winout.WindowOutputView): + def __init__(self, doc): + InteractiveCore.__init__(self) + winout.WindowOutputView.__init__(self, doc) + self.encoding = pywin.default_scintilla_encoding + + def _MakeColorizer(self): + return InteractiveFormatter(self) + + def OnInitialUpdate(self): + winout.WindowOutputView.OnInitialUpdate(self) + self.SetWordWrap() + self.Init() + + def HookHandlers(self): + winout.WindowOutputView.HookHandlers(self) + InteractiveCore.HookHandlers(self) + + +class CInteractivePython(winout.WindowOutput): + def __init__(self, makeDoc=None, makeFrame=None): + self.IsFinalDestroy = 0 + winout.WindowOutput.__init__( + self, + sectionProfile, + sectionProfile, + winout.flags.WQ_LINE, + 1, + None, + makeDoc, + makeFrame, + InteractiveView, + ) + self.Create() + + def OnViewDestroy(self, view): + if self.IsFinalDestroy: + view.OutputRelease() + winout.WindowOutput.OnViewDestroy(self, view) + + def Close(self): + self.IsFinalDestroy = 1 + winout.WindowOutput.Close(self) + + +class InteractiveFrame(winout.WindowOutputFrame): + def __init__(self): + self.lastActive = None + winout.WindowOutputFrame.__init__(self) + + def OnMDIActivate(self, bActive, wndActive, wndDeactive): + if bActive: + self.lastActive = wndDeactive + + +###################################################################### +## +## Dockable Window Support +## +###################################################################### +ID_DOCKED_INTERACTIVE_CONTROLBAR = 0xE802 + +DockedInteractiveViewParent = InteractiveView + + +class DockedInteractiveView(DockedInteractiveViewParent): + def HookHandlers(self): + DockedInteractiveViewParent.HookHandlers(self) + self.HookMessage(self.OnSetFocus, win32con.WM_SETFOCUS) + self.HookMessage(self.OnKillFocus, win32con.WM_KILLFOCUS) + + def OnSetFocus(self, msg): + self.GetParentFrame().SetActiveView(self) + return 1 + + def OnKillFocus(self, msg): + # If we are losing focus to another in this app, reset the main frame's active view. + hwnd = wparam = msg[2] + try: + wnd = win32ui.CreateWindowFromHandle(hwnd) + reset = wnd.GetTopLevelFrame() == self.GetTopLevelFrame() + except win32ui.error: + reset = 0 # Not my window + if reset: + self.GetParentFrame().SetActiveView(None) + return 1 + + def OnDestroy(self, msg): + newSize = self.GetWindowPlacement()[4] + pywin.framework.app.SaveWindowSize("Interactive Window", newSize, "docked") + try: + if self.GetParentFrame().GetActiveView == self: + self.GetParentFrame().SetActiveView(None) + except win32ui.error: + pass + try: + if win32ui.GetMainFrame().GetActiveView() == self: + win32ui.GetMainFrame().SetActiveView(None) + except win32ui.error: + pass + return DockedInteractiveViewParent.OnDestroy(self, msg) + + +class CDockedInteractivePython(CInteractivePython): + def __init__(self, dockbar): + self.bFirstCreated = 0 + self.dockbar = dockbar + CInteractivePython.__init__(self) + + def NeedRecreateWindow(self): + if self.bCreating: + return 0 + try: + frame = win32ui.GetMainFrame() + if frame.closing: + return 0 # Dieing! + except (win32ui.error, AttributeError): + return 0 # The app is dieing! + try: + cb = frame.GetControlBar(ID_DOCKED_INTERACTIVE_CONTROLBAR) + return not cb.IsWindowVisible() + except win32ui.error: + return 1 # Control bar does not exist! + + def RecreateWindow(self): + try: + dockbar = win32ui.GetMainFrame().GetControlBar( + ID_DOCKED_INTERACTIVE_CONTROLBAR + ) + win32ui.GetMainFrame().ShowControlBar(dockbar, 1, 1) + except win32ui.error: + CreateDockedInteractiveWindow() + + def Create(self): + self.bCreating = 1 + doc = InteractiveDocument(None, self.DoCreateDoc()) + view = DockedInteractiveView(doc) + defRect = pywin.framework.app.LoadWindowSize("Interactive Window", "docked") + if defRect[2] - defRect[0] == 0: + defRect = 0, 0, 500, 200 + style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_BORDER + id = 1050 # win32ui.AFX_IDW_PANE_FIRST + view.CreateWindow(self.dockbar, id, style, defRect) + view.OnInitialUpdate() + self.bFirstCreated = 1 + + self.currentView = doc.GetFirstView() + self.bCreating = 0 + if self.title: + doc.SetTitle(self.title) + + +# The factory we pass to the dockable window support. +def InteractiveViewCreator(parent): + global edit + edit = CDockedInteractivePython(parent) + return edit.currentView + + +def CreateDockedInteractiveWindow(): + # Later, the DockingBar should be capable of hosting multiple + # children. + from pywin.docking.DockingBar import DockingBar + + bar = DockingBar() + creator = InteractiveViewCreator + bar.CreateWindow( + win32ui.GetMainFrame(), + creator, + "Interactive Window", + ID_DOCKED_INTERACTIVE_CONTROLBAR, + ) + bar.SetBarStyle( + bar.GetBarStyle() + | afxres.CBRS_TOOLTIPS + | afxres.CBRS_FLYBY + | afxres.CBRS_SIZE_DYNAMIC + ) + bar.EnableDocking(afxres.CBRS_ALIGN_ANY) + win32ui.GetMainFrame().DockControlBar(bar, afxres.AFX_IDW_DOCKBAR_BOTTOM) + + +###################################################################### +# +# The public interface to this module. +# +###################################################################### +# No extra functionality now, but maybe later, so +# publicize these names. +InteractiveDocument = winout.WindowOutputDocument + +# We remember our one and only interactive window in the "edit" variable. +edit = None + + +def CreateInteractiveWindowUserPreference(makeDoc=None, makeFrame=None): + """Create some sort of interactive window if the user's preference say we should.""" + bCreate = LoadPreference("Show at startup", 1) + if bCreate: + CreateInteractiveWindow(makeDoc, makeFrame) + + +def CreateInteractiveWindow(makeDoc=None, makeFrame=None): + """Create a standard or docked interactive window unconditionally""" + assert edit is None, "Creating second interactive window!" + bDocking = LoadPreference("Docking", 0) + if bDocking: + CreateDockedInteractiveWindow() + else: + CreateMDIInteractiveWindow(makeDoc, makeFrame) + assert edit is not None, "Created interactive window, but did not set the global!" + edit.currentView.SetFocus() + + +def CreateMDIInteractiveWindow(makeDoc=None, makeFrame=None): + """Create a standard (non-docked) interactive window unconditionally""" + global edit + if makeDoc is None: + makeDoc = InteractiveDocument + if makeFrame is None: + makeFrame = InteractiveFrame + edit = CInteractivePython(makeDoc=makeDoc, makeFrame=makeFrame) + + +def DestroyInteractiveWindow(): + """Destroy the interactive window. + This is different to Closing the window, + which may automatically re-appear. Once destroyed, it can never be recreated, + and a complete new instance must be created (which the various other helper + functions will then do after making this call + """ + global edit + if edit is not None and edit.currentView is not None: + if edit.currentView.GetParentFrame() == win32ui.GetMainFrame(): + # It is docked - do nothing now (this is only called at shutdown!) + pass + else: + # It is a standard window - call Close on the container. + edit.Close() + edit = None + + +def CloseInteractiveWindow(): + """Close the interactive window, allowing it to be re-created on demand.""" + global edit + if edit is not None and edit.currentView is not None: + if edit.currentView.GetParentFrame() == win32ui.GetMainFrame(): + # It is docked, just hide the dock bar. + frame = win32ui.GetMainFrame() + cb = frame.GetControlBar(ID_DOCKED_INTERACTIVE_CONTROLBAR) + frame.ShowControlBar(cb, 0, 1) + else: + # It is a standard window - destroy the frame/view, allowing the object itself to remain. + edit.currentView.GetParentFrame().DestroyWindow() + + +def ToggleInteractiveWindow(): + """If the interactive window is visible, hide it, otherwise show it.""" + if edit is None: + CreateInteractiveWindow() + else: + if edit.NeedRecreateWindow(): + edit.RecreateWindow() + else: + # Close it, allowing a reopen. + CloseInteractiveWindow() + + +def ShowInteractiveWindow(): + """Shows (or creates if necessary) an interactive window""" + if edit is None: + CreateInteractiveWindow() + else: + if edit.NeedRecreateWindow(): + edit.RecreateWindow() + else: + parent = edit.currentView.GetParentFrame() + if parent == win32ui.GetMainFrame(): # It is docked. + edit.currentView.SetFocus() + else: # It is a "normal" window + edit.currentView.GetParentFrame().AutoRestore() + win32ui.GetMainFrame().MDIActivate(edit.currentView.GetParentFrame()) + + +def IsInteractiveWindowVisible(): + return edit is not None and not edit.NeedRecreateWindow() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/framework/intpyapp.py b/myenv/Lib/site-packages/pythonwin/pywin/framework/intpyapp.py new file mode 100644 index 000000000..48461dbab --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/framework/intpyapp.py @@ -0,0 +1,555 @@ +# intpyapp.py - Interactive Python application class +# +import os +import sys +import traceback + +import __main__ +import commctrl +import win32api +import win32con +import win32ui +from pywin.mfc import afxres, dialog + +from . import app, dbgcommands + +lastLocateFileName = ".py" # used in the "File/Locate" dialog... + + +# todo - _SetupSharedMenu should be moved to a framework class. +def _SetupSharedMenu_(self): + sharedMenu = self.GetSharedMenu() + from pywin.framework import toolmenu + + toolmenu.SetToolsMenu(sharedMenu) + from pywin.framework import help + + help.SetHelpMenuOtherHelp(sharedMenu) + + +from pywin.mfc import docview + +docview.DocTemplate._SetupSharedMenu_ = _SetupSharedMenu_ + + +class MainFrame(app.MainFrame): + def OnCreate(self, createStruct): + self.closing = 0 + if app.MainFrame.OnCreate(self, createStruct) == -1: + return -1 + style = ( + win32con.WS_CHILD + | afxres.CBRS_SIZE_DYNAMIC + | afxres.CBRS_TOP + | afxres.CBRS_TOOLTIPS + | afxres.CBRS_FLYBY + ) + + self.EnableDocking(afxres.CBRS_ALIGN_ANY) + + tb = win32ui.CreateToolBar(self, style | win32con.WS_VISIBLE) + tb.ModifyStyle(0, commctrl.TBSTYLE_FLAT) + tb.LoadToolBar(win32ui.IDR_MAINFRAME) + tb.EnableDocking(afxres.CBRS_ALIGN_ANY) + tb.SetWindowText("Standard") + self.DockControlBar(tb) + # Any other packages which use toolbars + from pywin.debugger.debugger import PrepareControlBars + + PrepareControlBars(self) + # Note "interact" also uses dockable windows, but they already happen + + # And a "Tools" menu on the main frame. + menu = self.GetMenu() + from . import toolmenu + + toolmenu.SetToolsMenu(menu, 2) + # And fix the "Help" menu on the main frame + from pywin.framework import help + + help.SetHelpMenuOtherHelp(menu) + + def OnClose(self): + try: + import pywin.debugger + + if ( + pywin.debugger.currentDebugger is not None + and pywin.debugger.currentDebugger.pumping + ): + try: + pywin.debugger.currentDebugger.close(1) + except: + traceback.print_exc() + return + except win32ui.error: + pass + self.closing = 1 + self.SaveBarState("ToolbarDefault") + self.SetActiveView(None) # Otherwise MFC's OnClose may _not_ prompt for save. + + from pywin.framework import help + + help.FinalizeHelp() + + self.DestroyControlBar(afxres.AFX_IDW_TOOLBAR) + self.DestroyControlBar(win32ui.ID_VIEW_TOOLBAR_DBG) + + return self._obj_.OnClose() + + def DestroyControlBar(self, id): + try: + bar = self.GetControlBar(id) + except win32ui.error: + return + bar.DestroyWindow() + + def OnCommand(self, wparam, lparam): + # By default, the current MDI child frame will process WM_COMMAND + # messages before any docked control bars - even if the control bar + # has focus. This is a problem for the interactive window when docked. + # Therefore, we detect the situation of a view having the main frame + # as its parent, and assume it must be a docked view (which it will in an MDI app) + try: + v = ( + self.GetActiveView() + ) # Raise an exception if none - good - then we want default handling + # Main frame _does_ have a current view (ie, a docking view) - see if it wants it. + if v.OnCommand(wparam, lparam): + return 1 + except (win32ui.error, AttributeError): + pass + return self._obj_.OnCommand(wparam, lparam) + + +class InteractivePythonApp(app.CApp): + # This works if necessary - just we dont need to override the Run method. + # def Run(self): + # return self._obj_.Run() + + def HookCommands(self): + app.CApp.HookCommands(self) + dbgcommands.DebuggerCommandHandler().HookCommands() + self.HookCommand(self.OnViewBrowse, win32ui.ID_VIEW_BROWSE) + self.HookCommand(self.OnFileImport, win32ui.ID_FILE_IMPORT) + self.HookCommand(self.OnFileCheck, win32ui.ID_FILE_CHECK) + self.HookCommandUpdate(self.OnUpdateFileCheck, win32ui.ID_FILE_CHECK) + self.HookCommand(self.OnFileRun, win32ui.ID_FILE_RUN) + self.HookCommand(self.OnFileLocate, win32ui.ID_FILE_LOCATE) + self.HookCommand(self.OnInteractiveWindow, win32ui.ID_VIEW_INTERACTIVE) + self.HookCommandUpdate( + self.OnUpdateInteractiveWindow, win32ui.ID_VIEW_INTERACTIVE + ) + self.HookCommand(self.OnViewOptions, win32ui.ID_VIEW_OPTIONS) + self.HookCommand(self.OnHelpIndex, afxres.ID_HELP_INDEX) + self.HookCommand(self.OnFileSaveAll, win32ui.ID_FILE_SAVE_ALL) + self.HookCommand(self.OnViewToolbarDbg, win32ui.ID_VIEW_TOOLBAR_DBG) + self.HookCommandUpdate(self.OnUpdateViewToolbarDbg, win32ui.ID_VIEW_TOOLBAR_DBG) + + def CreateMainFrame(self): + return MainFrame() + + def MakeExistingDDEConnection(self): + # Use DDE to connect to an existing instance + # Return None if no existing instance + try: + from . import intpydde + except ImportError: + # No dde support! + return None + conv = intpydde.CreateConversation(self.ddeServer) + try: + conv.ConnectTo("Pythonwin", "System") + return conv + except intpydde.error: + return None + + def InitDDE(self): + # Do all the magic DDE handling. + # Returns TRUE if we have pumped the arguments to our + # remote DDE app, and we should terminate. + try: + from . import intpydde + except ImportError: + self.ddeServer = None + intpydde = None + if intpydde is not None: + self.ddeServer = intpydde.DDEServer(self) + self.ddeServer.Create("Pythonwin", intpydde.CBF_FAIL_SELFCONNECTIONS) + try: + # If there is an existing instance, pump the arguments to it. + connection = self.MakeExistingDDEConnection() + if connection is not None: + connection.Exec("self.Activate()") + if self.ProcessArgs(sys.argv, connection) is None: + return 1 + except: + # It is too early to 'print' an exception - we + # don't have stdout setup yet! + win32ui.DisplayTraceback( + sys.exc_info(), " - error in DDE conversation with Pythonwin" + ) + return 1 + + def InitInstance(self): + # Allow "/nodde" and "/new" to optimize this! + if ( + "/nodde" not in sys.argv + and "/new" not in sys.argv + and "-nodde" not in sys.argv + and "-new" not in sys.argv + ): + if self.InitDDE(): + return 1 # A remote DDE client is doing it for us! + else: + self.ddeServer = None + + win32ui.SetRegistryKey( + "Python %s" % (sys.winver,) + ) # MFC automatically puts the main frame caption on! + app.CApp.InitInstance(self) + + # Create the taskbar icon + win32ui.CreateDebuggerThread() + + # Allow Pythonwin to host OCX controls. + win32ui.EnableControlContainer() + + # Display the interactive window if the user wants it. + from . import interact + + interact.CreateInteractiveWindowUserPreference() + + # Load the modules we use internally. + self.LoadSystemModules() + + # Load additional module the user may want. + self.LoadUserModules() + + # Load the ToolBar state near the end of the init process, as + # there may be Toolbar IDs created by the user or other modules. + # By now all these modules should be loaded, so all the toolbar IDs loaded. + try: + self.frame.LoadBarState("ToolbarDefault") + except win32ui.error: + # MFC sucks. It does essentially "GetDlgItem(x)->Something", so if the + # toolbar with ID x does not exist, MFC crashes! Pythonwin has a trap for this + # but I need to investigate more how to prevent it (AFAIK, ensuring all the + # toolbars are created by now _should_ stop it!) + pass + + # Finally process the command line arguments. + try: + self.ProcessArgs(sys.argv) + except: + # too early for printing anything. + win32ui.DisplayTraceback( + sys.exc_info(), " - error processing command line args" + ) + + def ExitInstance(self): + win32ui.DestroyDebuggerThread() + try: + from . import interact + + interact.DestroyInteractiveWindow() + except: + pass + if self.ddeServer is not None: + self.ddeServer.Shutdown() + self.ddeServer = None + return app.CApp.ExitInstance(self) + + def Activate(self): + # Bring to the foreground. Mainly used when another app starts up, it asks + # this one to activate itself, then it terminates. + frame = win32ui.GetMainFrame() + frame.SetForegroundWindow() + if frame.GetWindowPlacement()[1] == win32con.SW_SHOWMINIMIZED: + frame.ShowWindow(win32con.SW_RESTORE) + + def ProcessArgs(self, args, dde=None): + # If we are going to talk to a remote app via DDE, then + # activate it! + if ( + len(args) < 1 or not args[0] + ): # argv[0]=='' when started without args, just like Python.exe! + return + + i = 0 + while i < len(args): + argType = args[i] + i += 1 + if argType.startswith("-"): + # Support dash options. Slash options are misinterpreted by python init + # as path and not finding usually 'C:\\' ends up in sys.path[0] + argType = "/" + argType[1:] + if not argType.startswith("/"): + argType = win32ui.GetProfileVal( + "Python", "Default Arg Type", "/edit" + ).lower() + i -= 1 # arg is /edit's parameter + par = i < len(args) and args[i] or "MISSING" + if argType in ("/nodde", "/new", "-nodde", "-new"): + # Already handled + pass + elif argType.startswith("/goto:"): + gotoline = int(argType[len("/goto:") :]) + if dde: + dde.Exec( + "from pywin.framework import scriptutils\n" + "ed = scriptutils.GetActiveEditControl()\n" + "if ed: ed.SetSel(ed.LineIndex(%s - 1))" % gotoline + ) + else: + from . import scriptutils + + ed = scriptutils.GetActiveEditControl() + if ed: + ed.SetSel(ed.LineIndex(gotoline - 1)) + elif argType == "/edit": + # Load up the default application. + i += 1 + fname = win32api.GetFullPathName(par) + if not os.path.isfile(fname): + # if we don't catch this, OpenDocumentFile() (actually + # PyCDocument.SetPathName() in + # pywin.scintilla.document.CScintillaDocument.OnOpenDocument) + # segfaults Pythonwin on recent PY3 builds (b228) + win32ui.MessageBox( + "No such file: %s\n\nCommand Line: %s" + % (fname, win32api.GetCommandLine()), + "Open file for edit", + win32con.MB_ICONERROR, + ) + continue + if dde: + dde.Exec("win32ui.GetApp().OpenDocumentFile(%s)" % (repr(fname))) + else: + win32ui.GetApp().OpenDocumentFile(par) + elif argType == "/rundlg": + if dde: + dde.Exec( + "from pywin.framework import scriptutils;scriptutils.RunScript(%r, %r, 1)" + % (par, " ".join(args[i + 1 :])) + ) + else: + from . import scriptutils + + scriptutils.RunScript(par, " ".join(args[i + 1 :])) + return + elif argType == "/run": + if dde: + dde.Exec( + "from pywin.framework import scriptutils;scriptutils.RunScript(%r, %r, 0)" + % (par, " ".join(args[i + 1 :])) + ) + else: + from . import scriptutils + + scriptutils.RunScript(par, " ".join(args[i + 1 :]), 0) + return + elif argType == "/app": + raise RuntimeError( + "/app only supported for new instances of Pythonwin.exe" + ) + elif argType == "/dde": # Send arbitary command + if dde is not None: + dde.Exec(par) + else: + win32ui.MessageBox( + "The /dde command can only be used\r\nwhen Pythonwin is already running" + ) + i += 1 + else: + raise ValueError("Command line argument not recognised: %s" % argType) + + def LoadSystemModules(self): + self.DoLoadModules("pywin.framework.editor,pywin.framework.stdin") + + def LoadUserModules(self, moduleNames=None): + # Load the users modules. + if moduleNames is None: + default = "pywin.framework.sgrepmdi,pywin.framework.mdi_pychecker" + moduleNames = win32ui.GetProfileVal("Python", "Startup Modules", default) + self.DoLoadModules(moduleNames) + + def DoLoadModules(self, moduleNames): # ", sep string of module names. + if not moduleNames: + return + modules = moduleNames.split(",") + for module in modules: + try: + __import__(module) + except: # Catch em all, else the app itself dies! 'ImportError: + traceback.print_exc() + msg = 'Startup import of user module "%s" failed' % module + print(msg) + win32ui.MessageBox(msg) + + # + # DDE Callback + # + def OnDDECommand(self, command): + try: + exec(command + "\n") + except: + print("ERROR executing DDE command: ", command) + traceback.print_exc() + raise + + # + # General handlers + # + def OnViewBrowse(self, id, code): + "Called when ViewBrowse message is received" + from pywin.tools import browser + + obName = dialog.GetSimpleInput("Object", "__builtins__", "Browse Python Object") + if obName is None: + return + try: + browser.Browse(eval(obName, __main__.__dict__, __main__.__dict__)) + except NameError: + win32ui.MessageBox("This is no object with this name") + except AttributeError: + win32ui.MessageBox("The object has no attribute of that name") + except: + traceback.print_exc() + win32ui.MessageBox("This object can not be browsed") + + def OnFileImport(self, id, code): + "Called when a FileImport message is received. Import the current or specified file" + from . import scriptutils + + scriptutils.ImportFile() + + def OnFileCheck(self, id, code): + "Called when a FileCheck message is received. Check the current file." + from . import scriptutils + + scriptutils.CheckFile() + + def OnUpdateFileCheck(self, cmdui): + from . import scriptutils + + cmdui.Enable(scriptutils.GetActiveFileName(0) is not None) + + def OnFileRun(self, id, code): + "Called when a FileRun message is received." + from . import scriptutils + + showDlg = win32api.GetKeyState(win32con.VK_SHIFT) >= 0 + scriptutils.RunScript(None, None, showDlg) + + def OnFileLocate(self, id, code): + from . import scriptutils + + global lastLocateFileName # save the new version away for next time... + + name = dialog.GetSimpleInput( + "File name", lastLocateFileName, "Locate Python File" + ) + if name is None: # Cancelled. + return + lastLocateFileName = name + # if ".py" supplied, rip it off! + # should also check for .pys and .pyw + if lastLocateFileName[-3:].lower() == ".py": + lastLocateFileName = lastLocateFileName[:-3] + lastLocateFileName = lastLocateFileName.replace(".", "\\") + newName = scriptutils.LocatePythonFile(lastLocateFileName) + if newName is None: + win32ui.MessageBox("The file '%s' can not be located" % lastLocateFileName) + else: + win32ui.GetApp().OpenDocumentFile(newName) + + # Display all the "options" proprety pages we can find + def OnViewOptions(self, id, code): + win32ui.InitRichEdit() + sheet = dialog.PropertySheet("Pythonwin Options") + # Add property pages we know about that need manual work. + from pywin.dialogs import ideoptions + + sheet.AddPage(ideoptions.OptionsPropPage()) + + from . import toolmenu + + sheet.AddPage(toolmenu.ToolMenuPropPage()) + + # Get other dynamic pages from templates. + pages = [] + for template in self.GetDocTemplateList(): + try: + # Dont actually call the function with the exception handler. + getter = template.GetPythonPropertyPages + except AttributeError: + # Template does not provide property pages! + continue + pages = pages + getter() + + # Debugger template goes at the end + try: + from pywin.debugger import configui + except ImportError: + configui = None + if configui is not None: + pages.append(configui.DebuggerOptionsPropPage()) + # Now simply add the pages, and display the dialog. + for page in pages: + sheet.AddPage(page) + + if sheet.DoModal() == win32con.IDOK: + win32ui.SetStatusText("Applying configuration changes...", 1) + win32ui.DoWaitCursor(1) + # Tell every Window in our app that win.ini has changed! + win32ui.GetMainFrame().SendMessageToDescendants( + win32con.WM_WININICHANGE, 0, 0 + ) + win32ui.DoWaitCursor(0) + + def OnInteractiveWindow(self, id, code): + # toggle the existing state. + from . import interact + + interact.ToggleInteractiveWindow() + + def OnUpdateInteractiveWindow(self, cmdui): + try: + interact = sys.modules["pywin.framework.interact"] + state = interact.IsInteractiveWindowVisible() + except KeyError: # Interactive module hasnt ever been imported. + state = 0 + cmdui.Enable() + cmdui.SetCheck(state) + + def OnFileSaveAll(self, id, code): + # Only attempt to save editor documents. + from pywin.framework.editor import editorTemplate + + num = 0 + for doc in editorTemplate.GetDocumentList(): + if doc.IsModified() and doc.GetPathName(): + num = num = 1 + doc.OnSaveDocument(doc.GetPathName()) + win32ui.SetStatusText("%d documents saved" % num, 1) + + def OnViewToolbarDbg(self, id, code): + if code == 0: + return not win32ui.GetMainFrame().OnBarCheck(id) + + def OnUpdateViewToolbarDbg(self, cmdui): + win32ui.GetMainFrame().OnUpdateControlBarMenu(cmdui) + cmdui.Enable(1) + + def OnHelpIndex(self, id, code): + from . import help + + help.SelectAndRunHelpFile() + + +# As per the comments in app.py, this use is depreciated. +# app.AppBuilder = InteractivePythonApp + +# Now all we do is create the application +thisApp = InteractivePythonApp() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/framework/intpydde.py b/myenv/Lib/site-packages/pythonwin/pywin/framework/intpydde.py new file mode 100644 index 000000000..1f869b0f6 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/framework/intpydde.py @@ -0,0 +1,60 @@ +# DDE support for Pythonwin +# +# Seems to work fine (in the context that IE4 seems to have broken +# DDE on _all_ NT4 machines I have tried, but only when a "Command Prompt" window +# is open. Strange, but true. If you have problems with this, close all Command Prompts! + + +import sys +import traceback + +import win32api +import win32ui +from dde import * +from pywin.mfc import object + + +class DDESystemTopic(object.Object): + def __init__(self, app): + self.app = app + object.Object.__init__(self, CreateServerSystemTopic()) + + def Exec(self, data): + try: + # print "Executing", cmd + self.app.OnDDECommand(data) + except: + t, v, tb = sys.exc_info() + # The DDE Execution failed. + print("Error executing DDE command.") + traceback.print_exception(t, v, tb) + return 0 + + +class DDEServer(object.Object): + def __init__(self, app): + self.app = app + object.Object.__init__(self, CreateServer()) + self.topic = self.item = None + + def CreateSystemTopic(self): + return DDESystemTopic(self.app) + + def Shutdown(self): + self._obj_.Shutdown() + self._obj_.Destroy() + if self.topic is not None: + self.topic.Destroy() + self.topic = None + if self.item is not None: + self.item.Destroy() + self.item = None + + def OnCreate(self): + return 1 + + def Status(self, msg): + try: + win32ui.SetStatusText(msg) + except win32ui.error: + pass diff --git a/myenv/Lib/site-packages/pythonwin/pywin/framework/mdi_pychecker.py b/myenv/Lib/site-packages/pythonwin/pywin/framework/mdi_pychecker.py new file mode 100644 index 000000000..c89f33b9b --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/framework/mdi_pychecker.py @@ -0,0 +1,849 @@ +###################################################################### +## +## The Pychecker MDI Plug-In UserModule for Pythonwin +## +## contributed by Robert Kiendl +## +## Style is similar to (and inherited) from the SGrepMDI UserModule +## +## Usage: +## +## Start Pychecker on current file: Menu/File/New../Pychecker. +## Use it: Jump to Pychecker warning source lines by double-click. +## Auto-add "#$pycheck_no" / "#$pycheck_no=specific-re-pattern" tags +## to source lines by context/right-mouse-click on warning lines. +## +## It requires pychecker installed and the pychecker.bat to be on +## the PATH. Example pychecker.bat: +## +## REM pychecker.bat +## C:\bin\python.exe C:\PYTHON23\Lib\site-packages\pychecker\checker.py %1 %2 %3 %4 %5 %6 %7 %8 %9 +## +## Adding it as default module in PythonWin: +## +## +++ ./intpyapp.py 2006-10-02 17:59:32.974161600 +0200 +## @@ -272,7 +282,7 @@ +## def LoadUserModules(self, moduleNames = None): +## # Load the users modules. +## if moduleNames is None: +## - default = "sgrepmdi" +## + default = "sgrepmdi,mdi_pychecker" +## moduleNames=win32ui.GetProfileVal('Python','Startup Modules',default) +## self.DoLoadModules(moduleNames) +## +###################################################################### + +import glob +import os +import re +import sys +import time + +import win32api +import win32con +import win32ui +from pywin.mfc import dialog, docview, window + +from . import scriptutils + + +def getsubdirs(d): + dlist = [] + flist = glob.glob(d + "\\*") + for f in flist: + if os.path.isdir(f): + dlist.append(f) + dlist = dlist + getsubdirs(f) + return dlist + + +class dirpath: + def __init__(self, str, recurse=0): + dp = str.split(";") + dirs = {} + for d in dp: + if os.path.isdir(d): + d = d.lower() + if d not in dirs: + dirs[d] = None + if recurse: + subdirs = getsubdirs(d) + for sd in subdirs: + sd = sd.lower() + if sd not in dirs: + dirs[sd] = None + elif os.path.isfile(d): + pass + else: + x = None + if d in os.environ: + x = dirpath(os.environ[d]) + elif d[:5] == "HKEY_": + keystr = d.split("\\") + try: + root = eval("win32con." + keystr[0]) + except: + win32ui.MessageBox( + "Can't interpret registry key name '%s'" % keystr[0] + ) + try: + subkey = "\\".join(keystr[1:]) + val = win32api.RegQueryValue(root, subkey) + if val: + x = dirpath(val) + else: + win32ui.MessageBox( + "Registry path '%s' did not return a path entry" % d + ) + except: + win32ui.MessageBox( + "Can't interpret registry key value: %s" % keystr[1:] + ) + else: + win32ui.MessageBox("Directory '%s' not found" % d) + if x: + for xd in x: + if xd not in dirs: + dirs[xd] = None + if recurse: + subdirs = getsubdirs(xd) + for sd in subdirs: + sd = sd.lower() + if sd not in dirs: + dirs[sd] = None + self.dirs = [] + for d in dirs.keys(): + self.dirs.append(d) + + def __getitem__(self, key): + return self.dirs[key] + + def __len__(self): + return len(self.dirs) + + def __setitem__(self, key, value): + self.dirs[key] = value + + def __delitem__(self, key): + del self.dirs[key] + + def __getslice__(self, lo, hi): + return self.dirs[lo:hi] + + def __setslice__(self, lo, hi, seq): + self.dirs[lo:hi] = seq + + def __delslice__(self, lo, hi): + del self.dirs[lo:hi] + + def __add__(self, other): + if type(other) == type(self) or type(other) == type([]): + return self.dirs + other.dirs + + def __radd__(self, other): + if type(other) == type(self) or type(other) == type([]): + return other.dirs + self.dirs + + +# Group(1) is the filename, group(2) is the lineno. +# regexGrepResult=regex.compile("^\\([a-zA-Z]:.*\\)(\\([0-9]+\\))") +# regexGrep=re.compile(r"^([a-zA-Z]:[^(]*)\((\d+)\)") +regexGrep = re.compile(r"^(..[^\(:]+)?[\(:](\d+)[\):]:?\s*(.*)") + +# these are the atom numbers defined by Windows for basic dialog controls + +BUTTON = 0x80 +EDIT = 0x81 +STATIC = 0x82 +LISTBOX = 0x83 +SCROLLBAR = 0x84 +COMBOBOX = 0x85 + + +class TheTemplate(docview.RichEditDocTemplate): + def __init__(self): + docview.RichEditDocTemplate.__init__( + self, win32ui.IDR_TEXTTYPE, TheDocument, TheFrame, TheView + ) + self.SetDocStrings( + "\nPychecker\nPychecker\nPychecker params (*.pychecker)\n.pychecker\n\n\n" + ) + win32ui.GetApp().AddDocTemplate(self) + self.docparams = None + + def MatchDocType(self, fileName, fileType): + doc = self.FindOpenDocument(fileName) + if doc: + return doc + ext = os.path.splitext(fileName)[1].lower() + if ext == ".pychecker": + return win32ui.CDocTemplate_Confidence_yesAttemptNative + return win32ui.CDocTemplate_Confidence_noAttempt + + def setParams(self, params): + self.docparams = params + + def readParams(self): + tmp = self.docparams + self.docparams = None + return tmp + + +class TheFrame(window.MDIChildWnd): + # The template and doc params will one day be removed. + def __init__(self, wnd=None): + window.MDIChildWnd.__init__(self, wnd) + + +class TheDocument(docview.RichEditDoc): + def __init__(self, template): + docview.RichEditDoc.__init__(self, template) + self.dirpattern = "" + self.filpattern = "" + self.greppattern = "" + self.casesensitive = 1 + self.recurse = 1 + self.verbose = 0 + + def OnOpenDocument(self, fnm): + # this bizarre stuff with params is so right clicking in a result window + # and starting a new grep can communicate the default parameters to the + # new grep. + try: + params = open(fnm, "r").read() + except: + params = None + self.setInitParams(params) + return self.OnNewDocument() + + def OnCloseDocument(self): + try: + win32ui.GetApp().DeleteIdleHandler(self.idleHandler) + except: + pass + return self._obj_.OnCloseDocument() + + def saveInitParams(self): + # Only save the flags, not the text boxes. + paramstr = "\t\t\t%d\t%d" % (self.casesensitive, self.recurse) + win32ui.WriteProfileVal("Pychecker", "Params", paramstr) + + def setInitParams(self, paramstr): + if paramstr is None: + paramstr = win32ui.GetProfileVal("Pychecker", "Params", "\t\t\t1\t0\t0") + params = paramstr.split("\t") + if len(params) < 3: + params = params + [""] * (3 - len(params)) + if len(params) < 6: + params = params + [0] * (6 - len(params)) + self.dirpattern = params[0] + self.filpattern = params[1] + self.greppattern = params[2] or "-#1000 --only" + self.casesensitive = int(params[3]) + self.recurse = int(params[4]) + self.verbose = int(params[5]) + # setup some reasonable defaults. + if not self.dirpattern: + try: + editor = win32ui.GetMainFrame().MDIGetActive()[0].GetEditorView() + self.dirpattern = os.path.abspath( + os.path.dirname(editor.GetDocument().GetPathName()) + ) + except (AttributeError, win32ui.error): + self.dirpattern = os.getcwd() + if not self.filpattern: + try: + editor = win32ui.GetMainFrame().MDIGetActive()[0].GetEditorView() + self.filpattern = editor.GetDocument().GetPathName() + except AttributeError: + self.filpattern = "*.py" + + def OnNewDocument(self): + if self.dirpattern == "": + self.setInitParams(greptemplate.readParams()) + d = TheDialog( + self.dirpattern, + self.filpattern, + self.greppattern, + self.casesensitive, + self.recurse, + self.verbose, + ) + if d.DoModal() == win32con.IDOK: + self.dirpattern = d["dirpattern"] + self.filpattern = d["filpattern"] + self.greppattern = d["greppattern"] + # self.casesensitive = d['casesensitive'] + # self.recurse = d['recursive'] + # self.verbose = d['verbose'] + self.doSearch() + self.saveInitParams() + return 1 + return 0 # cancelled - return zero to stop frame creation. + + def doSearch(self): + self.dp = dirpath(self.dirpattern, self.recurse) + self.SetTitle( + "Pychecker Run '%s' (options: %s)" % (self.filpattern, self.greppattern) + ) + # self.text = [] + self.GetFirstView().Append( + "#Pychecker Run in " + self.dirpattern + " %s\n" % time.asctime() + ) + if self.verbose: + self.GetFirstView().Append("# =" + repr(self.dp.dirs) + "\n") + self.GetFirstView().Append("# Files " + self.filpattern + "\n") + self.GetFirstView().Append("# Options " + self.greppattern + "\n") + self.fplist = self.filpattern.split(";") + self.GetFirstView().Append( + "# Running... ( double click on result lines in order to jump to the source code ) \n" + ) + win32ui.SetStatusText("Pychecker running. Please wait...", 0) + self.dpndx = self.fpndx = 0 + self.fndx = -1 + if not self.dp: + self.GetFirstView().Append( + "# ERROR: '%s' does not resolve to any search locations" + % self.dirpattern + ) + self.SetModifiedFlag(0) + else: + ##self.flist = glob.glob(self.dp[0]+'\\'+self.fplist[0]) + import operator + + self.flist = reduce(operator.add, list(map(glob.glob, self.fplist))) + # import pywin.debugger;pywin.debugger.set_trace() + self.startPycheckerRun() + + def idleHandler(self, handler, count): + import time + + time.sleep(0.001) + if self.result != None: + win32ui.GetApp().DeleteIdleHandler(self.idleHandler) + return 0 + return 1 # more + + def startPycheckerRun(self): + self.result = None + old = win32api.SetCursor(win32api.LoadCursor(0, win32con.IDC_APPSTARTING)) + win32ui.GetApp().AddIdleHandler(self.idleHandler) + import _thread + + _thread.start_new(self.threadPycheckerRun, ()) + ##win32api.SetCursor(old) + + def threadPycheckerRun(self): + result = "" + rc = -1 + try: + options = self.greppattern + files = " ".join(self.flist) + # Recently MarkH has failed to run pychecker without it having + # been explicitly installed - so we assume it is and locate it + # from its default location. + # Step1 - get python.exe + py = os.path.join(sys.prefix, "python.exe") + if not os.path.isfile(py): + if "64 bit" in sys.version: + py = os.path.join(sys.prefix, "PCBuild", "amd64", "python.exe") + else: + py = os.path.join(sys.prefix, "PCBuild", "python.exe") + try: + py = win32api.GetShortPathName(py) + except win32api.error: + py = "" + # Find checker.py + import sysconfig + + pychecker = os.path.join( + sysconfig.get_paths()["purelib"], "pychecker", "checker.py" + ) + if not os.path.isfile(py): + result = "Can't find python.exe!\n" + elif not os.path.isfile(pychecker): + result = ( + "Can't find checker.py - please install pychecker " + "(or run 'setup.py install' if you have the source version)\n" + ) + else: + cmd = '%s "%s" %s %s 2>&1' % (py, pychecker, options, files) + ##fin,fout,ferr=os.popen3(cmd) + ##result=ferr.read()+fout.read() + result = os.popen(cmd).read() + ##rc=f.close() + self.GetFirstView().Append(result) + finally: + self.result = result + print("== Pychecker run finished ==") + self.GetFirstView().Append("\n" + "== Pychecker run finished ==") + self.SetModifiedFlag(0) + + def _inactive_idleHandler(self, handler, count): + self.fndx = self.fndx + 1 + if self.fndx < len(self.flist): + f = self.flist[self.fndx] + if self.verbose: + self.GetFirstView().Append("# .." + f + "\n") + win32ui.SetStatusText("Searching " + f, 0) + lines = open(f, "r").readlines() + for i in range(len(lines)): + line = lines[i] + if self.pat.search(line) != None: + self.GetFirstView().Append(f + "(" + repr(i + 1) + ") " + line) + else: + self.fndx = -1 + self.fpndx = self.fpndx + 1 + if self.fpndx < len(self.fplist): + self.flist = glob.glob( + self.dp[self.dpndx] + "\\" + self.fplist[self.fpndx] + ) + else: + self.fpndx = 0 + self.dpndx = self.dpndx + 1 + if self.dpndx < len(self.dp): + self.flist = glob.glob( + self.dp[self.dpndx] + "\\" + self.fplist[self.fpndx] + ) + else: + win32ui.SetStatusText("Search complete.", 0) + self.SetModifiedFlag(0) # default to not modified. + try: + win32ui.GetApp().DeleteIdleHandler(self.idleHandler) + except: + pass + return 0 + return 1 + + def GetParams(self): + return ( + self.dirpattern + + "\t" + + self.filpattern + + "\t" + + self.greppattern + + "\t" + + repr(self.casesensitive) + + "\t" + + repr(self.recurse) + + "\t" + + repr(self.verbose) + ) + + def OnSaveDocument(self, filename): + # print 'OnSaveDocument() filename=',filename + savefile = open(filename, "wb") + txt = self.GetParams() + "\n" + # print 'writing',txt + savefile.write(txt) + savefile.close() + self.SetModifiedFlag(0) + return 1 + + +ID_OPEN_FILE = 0xE500 +ID_PYCHECKER = 0xE501 +ID_SAVERESULTS = 0x502 +ID_TRYAGAIN = 0x503 +ID_ADDCOMMENT = 0x504 +ID_ADDPYCHECKNO2 = 0x505 + + +class TheView(docview.RichEditView): + def __init__(self, doc): + docview.RichEditView.__init__(self, doc) + self.SetWordWrap(win32ui.CRichEditView_WrapNone) + self.HookHandlers() + + def OnInitialUpdate(self): + rc = self._obj_.OnInitialUpdate() + format = (-402653169, 0, 200, 0, 0, 0, 49, "Courier New") + self.SetDefaultCharFormat(format) + return rc + + def HookHandlers(self): + self.HookMessage(self.OnRClick, win32con.WM_RBUTTONDOWN) + self.HookCommand(self.OnCmdOpenFile, ID_OPEN_FILE) + self.HookCommand(self.OnCmdThe, ID_PYCHECKER) + self.HookCommand(self.OnCmdSave, ID_SAVERESULTS) + self.HookCommand(self.OnTryAgain, ID_TRYAGAIN) + self.HookCommand(self.OnAddComment, ID_ADDCOMMENT) + self.HookCommand(self.OnAddComment, ID_ADDPYCHECKNO2) + self.HookMessage(self.OnLDblClick, win32con.WM_LBUTTONDBLCLK) + + def OnLDblClick(self, params): + line = self.GetLine() + regexGrepResult = regexGrep.match(line) + if regexGrepResult: + fname = regexGrepResult.group(1) + line = int(regexGrepResult.group(2)) + scriptutils.JumpToDocument(fname, line) + return 0 # dont pass on + return 1 # pass it on by default. + + def OnRClick(self, params): + menu = win32ui.CreatePopupMenu() + flags = win32con.MF_STRING | win32con.MF_ENABLED + lineno = self._obj_.LineFromChar(-1) # selection or current line + line = self._obj_.GetLine(lineno) + regexGrepResult = regexGrep.match(line) + charstart, charend = self._obj_.GetSel() + if regexGrepResult: + self.fnm = regexGrepResult.group(1) + self.lnnum = int(regexGrepResult.group(2)) + menu.AppendMenu(flags, ID_OPEN_FILE, "&Open " + self.fnm) + menu.AppendMenu( + flags, ID_ADDCOMMENT, "&Add to source: Comment Tag/#$pycheck_no .." + ) + menu.AppendMenu( + flags, + ID_ADDPYCHECKNO2, + "&Add to source: Specific #$pycheck_no=%(errtext)s ..", + ) + menu.AppendMenu(win32con.MF_SEPARATOR) + menu.AppendMenu(flags, ID_TRYAGAIN, "&Try Again") + menu.AppendMenu(flags, win32ui.ID_EDIT_CUT, "Cu&t") + menu.AppendMenu(flags, win32ui.ID_EDIT_COPY, "&Copy") + menu.AppendMenu(flags, win32ui.ID_EDIT_PASTE, "&Paste") + menu.AppendMenu(flags, win32con.MF_SEPARATOR) + menu.AppendMenu(flags, win32ui.ID_EDIT_SELECT_ALL, "&Select all") + menu.AppendMenu(flags, win32con.MF_SEPARATOR) + menu.AppendMenu(flags, ID_SAVERESULTS, "Sa&ve results") + menu.TrackPopupMenu(params[5]) + return 0 + + def OnAddComment(self, cmd, code): + addspecific = cmd == ID_ADDPYCHECKNO2 + _ = list(self.GetSel()) + _.sort() + start, end = _ + line_start, line_end = self.LineFromChar(start), self.LineFromChar(end) + first = 1 + for i in range(line_start, line_end + 1): + line = self.GetLine(i) + m = regexGrep.match(line) + if m: + if first: + first = 0 + cmnt = dialog.GetSimpleInput( + "Add to %s lines" % (line_end - line_start + 1), + addspecific + and " #$pycheck_no=%(errtext)s" + or " #$pycheck_no", + ) + if not cmnt: + return 0 + ##import pywin.debugger;pywin.debugger.set_trace() + fname = m.group(1) + line = int(m.group(2)) + view = scriptutils.JumpToDocument(fname, line) + pos = view.LineIndex(line) - 1 + if view.GetTextRange(pos - 1, pos) in ("\r", "\n"): + pos -= 1 + view.SetSel(pos, pos) + errtext = m.group(3) + if start != end and line_start == line_end: + errtext = self.GetSelText() + errtext = repr(re.escape(errtext).replace("\ ", " ")) + view.ReplaceSel(addspecific and cmnt % locals() or cmnt) + return 0 + + def OnCmdOpenFile(self, cmd, code): + doc = win32ui.GetApp().OpenDocumentFile(self.fnm) + if doc: + vw = doc.GetFirstView() + # hope you have an editor that implements GotoLine()! + try: + vw.GotoLine(int(self.lnnum)) + except: + pass + return 0 + + def OnCmdThe(self, cmd, code): + curparamsstr = self.GetDocument().GetParams() + params = curparamsstr.split("\t") + params[2] = self.sel + greptemplate.setParams("\t".join(params)) + greptemplate.OpenDocumentFile() + return 0 + + def OnTryAgain(self, cmd, code): + greptemplate.setParams(self.GetDocument().GetParams()) + greptemplate.OpenDocumentFile() + return 0 + + def OnCmdSave(self, cmd, code): + flags = win32con.OFN_OVERWRITEPROMPT + dlg = win32ui.CreateFileDialog( + 0, None, None, flags, "Text Files (*.txt)|*.txt||", self + ) + dlg.SetOFNTitle("Save Results As") + if dlg.DoModal() == win32con.IDOK: + pn = dlg.GetPathName() + self._obj_.SaveFile(pn) + return 0 + + def Append(self, strng): + numlines = self.GetLineCount() + endpos = self.LineIndex(numlines - 1) + len(self.GetLine(numlines - 1)) + self.SetSel(endpos, endpos) + self.ReplaceSel(strng) + + +class TheDialog(dialog.Dialog): + def __init__(self, dp, fp, gp, cs, r, v): + style = ( + win32con.DS_MODALFRAME + | win32con.WS_POPUP + | win32con.WS_VISIBLE + | win32con.WS_CAPTION + | win32con.WS_SYSMENU + | win32con.DS_SETFONT + ) + CS = win32con.WS_CHILD | win32con.WS_VISIBLE + tmp = [ + ["Pychecker Run", (0, 0, 210, 90), style, None, (8, "MS Sans Serif")], + ] + tmp.append([STATIC, "Files:", -1, (7, 7, 50, 9), CS]) + tmp.append( + [ + EDIT, + gp, + 103, + (52, 7, 144, 11), + CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER, + ] + ) + tmp.append([STATIC, "Directories:", -1, (7, 20, 50, 9), CS]) + tmp.append( + [ + EDIT, + dp, + 102, + (52, 20, 128, 11), + CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER, + ] + ) + tmp.append( + [ + BUTTON, + "...", + 110, + (182, 20, 16, 11), + CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP, + ] + ) + tmp.append([STATIC, "Options:", -1, (7, 33, 50, 9), CS]) + tmp.append( + [ + EDIT, + fp, + 101, + (52, 33, 128, 11), + CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER, + ] + ) + tmp.append( + [ + BUTTON, + "...", + 111, + (182, 33, 16, 11), + CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP, + ] + ) + # tmp.append([BUTTON,'Case sensitive', 104, (7, 45, 72, 9), CS | win32con.BS_AUTOCHECKBOX | win32con.BS_LEFTTEXT| win32con.WS_TABSTOP]) + # tmp.append([BUTTON,'Subdirectories', 105, (7, 56, 72, 9), CS | win32con.BS_AUTOCHECKBOX | win32con.BS_LEFTTEXT| win32con.WS_TABSTOP]) + # tmp.append([BUTTON,'Verbose', 106, (7, 67, 72, 9), CS | win32con.BS_AUTOCHECKBOX | win32con.BS_LEFTTEXT| win32con.WS_TABSTOP]) + tmp.append( + [ + BUTTON, + "OK", + win32con.IDOK, + (166, 53, 32, 12), + CS | win32con.BS_DEFPUSHBUTTON | win32con.WS_TABSTOP, + ] + ) + tmp.append( + [ + BUTTON, + "Cancel", + win32con.IDCANCEL, + (166, 67, 32, 12), + CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP, + ] + ) + dialog.Dialog.__init__(self, tmp) + self.AddDDX(101, "greppattern") + self.AddDDX(102, "dirpattern") + self.AddDDX(103, "filpattern") + # self.AddDDX(104,'casesensitive') + # self.AddDDX(105,'recursive') + # self.AddDDX(106,'verbose') + self._obj_.data["greppattern"] = gp + self._obj_.data["dirpattern"] = dp + self._obj_.data["filpattern"] = fp + # self._obj_.data['casesensitive'] = cs + # self._obj_.data['recursive'] = r + # self._obj_.data['verbose'] = v + self.HookCommand(self.OnMoreDirectories, 110) + self.HookCommand(self.OnMoreFiles, 111) + + def OnMoreDirectories(self, cmd, code): + self.getMore("Pychecker\\Directories", "dirpattern") + + def OnMoreFiles(self, cmd, code): + self.getMore("Pychecker\\File Types", "filpattern") + + def getMore(self, section, key): + self.UpdateData(1) + # get the items out of the ini file + ini = win32ui.GetProfileFileName() + secitems = win32api.GetProfileSection(section, ini) + items = [] + for secitem in secitems: + items.append(secitem.split("=")[1]) + dlg = TheParamsDialog(items) + if dlg.DoModal() == win32con.IDOK: + itemstr = ";".join(dlg.getItems()) + self._obj_.data[key] = itemstr + # update the ini file with dlg.getNew() + i = 0 + newitems = dlg.getNew() + if newitems: + items = items + newitems + for item in items: + win32api.WriteProfileVal(section, repr(i), item, ini) + i = i + 1 + self.UpdateData(0) + + def OnOK(self): + self.UpdateData(1) + for id, name in ( + (101, "greppattern"), + (102, "dirpattern"), + (103, "filpattern"), + ): + if not self[name]: + self.GetDlgItem(id).SetFocus() + win32api.MessageBeep() + win32ui.SetStatusText("Please enter a value") + return + self._obj_.OnOK() + + +class TheParamsDialog(dialog.Dialog): + def __init__(self, items): + self.items = items + self.newitems = [] + style = ( + win32con.DS_MODALFRAME + | win32con.WS_POPUP + | win32con.WS_VISIBLE + | win32con.WS_CAPTION + | win32con.WS_SYSMENU + | win32con.DS_SETFONT + ) + CS = win32con.WS_CHILD | win32con.WS_VISIBLE + tmp = [ + [ + "Pychecker Parameters", + (0, 0, 205, 100), + style, + None, + (8, "MS Sans Serif"), + ], + ] + tmp.append( + [ + LISTBOX, + "", + 107, + (7, 7, 150, 72), + CS + | win32con.LBS_MULTIPLESEL + | win32con.LBS_STANDARD + | win32con.LBS_HASSTRINGS + | win32con.WS_TABSTOP + | win32con.LBS_NOTIFY, + ] + ) + tmp.append( + [ + BUTTON, + "OK", + win32con.IDOK, + (167, 7, 32, 12), + CS | win32con.BS_DEFPUSHBUTTON | win32con.WS_TABSTOP, + ] + ) + tmp.append( + [ + BUTTON, + "Cancel", + win32con.IDCANCEL, + (167, 23, 32, 12), + CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP, + ] + ) + tmp.append([STATIC, "New:", -1, (2, 83, 15, 12), CS]) + tmp.append( + [ + EDIT, + "", + 108, + (18, 83, 139, 12), + CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER, + ] + ) + tmp.append( + [ + BUTTON, + "Add", + 109, + (167, 83, 32, 12), + CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP, + ] + ) + dialog.Dialog.__init__(self, tmp) + self.HookCommand(self.OnAddItem, 109) + self.HookCommand(self.OnListDoubleClick, 107) + + def OnInitDialog(self): + lb = self.GetDlgItem(107) + for item in self.items: + lb.AddString(item) + return self._obj_.OnInitDialog() + + def OnAddItem(self, cmd, code): + eb = self.GetDlgItem(108) + item = eb.GetLine(0) + self.newitems.append(item) + lb = self.GetDlgItem(107) + i = lb.AddString(item) + lb.SetSel(i, 1) + return 1 + + def OnListDoubleClick(self, cmd, code): + if code == win32con.LBN_DBLCLK: + self.OnOK() + return 1 + + def OnOK(self): + lb = self.GetDlgItem(107) + self.selections = lb.GetSelTextItems() + self._obj_.OnOK() + + def getItems(self): + return self.selections + + def getNew(self): + return self.newitems + + +try: + win32ui.GetApp().RemoveDocTemplate(greptemplate) +except NameError: + pass + +greptemplate = TheTemplate() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/framework/scriptutils.py b/myenv/Lib/site-packages/pythonwin/pywin/framework/scriptutils.py new file mode 100644 index 000000000..e5bc57c8f --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/framework/scriptutils.py @@ -0,0 +1,688 @@ +""" +Various utilities for running/importing a script +""" +import bdb +import linecache +import os +import sys +import traceback + +import __main__ +import win32api +import win32con +import win32ui +from pywin.mfc import dialog +from pywin.mfc.docview import TreeView + +from .cmdline import ParseArgs + +RS_DEBUGGER_NONE = 0 # Dont run under the debugger. +RS_DEBUGGER_STEP = 1 # Start stepping under the debugger +RS_DEBUGGER_GO = 2 # Just run under the debugger, stopping only at break-points. +RS_DEBUGGER_PM = 3 # Dont run under debugger, but do post-mortem analysis on exception. + +debugging_options = """No debugging +Step-through in the debugger +Run in the debugger +Post-Mortem of unhandled exceptions""".split( + "\n" +) + +byte_cr = "\r".encode("ascii") +byte_lf = "\n".encode("ascii") +byte_crlf = "\r\n".encode("ascii") + + +# A dialog box for the "Run Script" command. +class DlgRunScript(dialog.Dialog): + "A class for the 'run script' dialog" + + def __init__(self, bHaveDebugger): + dialog.Dialog.__init__(self, win32ui.IDD_RUN_SCRIPT) + self.AddDDX(win32ui.IDC_EDIT1, "script") + self.AddDDX(win32ui.IDC_EDIT2, "args") + self.AddDDX(win32ui.IDC_COMBO1, "debuggingType", "i") + self.HookCommand(self.OnBrowse, win32ui.IDC_BUTTON2) + self.bHaveDebugger = bHaveDebugger + + def OnInitDialog(self): + rc = dialog.Dialog.OnInitDialog(self) + cbo = self.GetDlgItem(win32ui.IDC_COMBO1) + for o in debugging_options: + cbo.AddString(o) + cbo.SetCurSel(self["debuggingType"]) + if not self.bHaveDebugger: + cbo.EnableWindow(0) + + def OnBrowse(self, id, code): + if code != 0: # BN_CLICKED + return 1 + openFlags = win32con.OFN_OVERWRITEPROMPT | win32con.OFN_FILEMUSTEXIST + dlg = win32ui.CreateFileDialog( + 1, None, None, openFlags, "Python Scripts (*.py)|*.py||", self + ) + dlg.SetOFNTitle("Run Script") + if dlg.DoModal() != win32con.IDOK: + return 0 + self["script"] = dlg.GetPathName() + self.UpdateData(0) + return 0 + + +def GetDebugger(): + """Get the default Python debugger. Returns the debugger, or None. + + It is assumed the debugger has a standard "pdb" defined interface. + Currently always returns the 'pywin.debugger' debugger, or None + (pdb is _not_ returned as it is not effective in this GUI environment) + """ + try: + import pywin.debugger + + return pywin.debugger + except ImportError: + return None + + +def IsOnPythonPath(path): + "Given a path only, see if it is on the Pythonpath. Assumes path is a full path spec." + # must check that the command line arg's path is in sys.path + for syspath in sys.path: + try: + # Python 1.5 and later allows an empty sys.path entry. + if syspath and win32ui.FullPath(syspath) == path: + return 1 + except win32ui.error as details: + print( + "Warning: The sys.path entry '%s' is invalid\n%s" % (syspath, details) + ) + return 0 + + +def GetPackageModuleName(fileName): + """Given a filename, return (module name, new path). + eg - given "c:\a\b\c\my.py", return ("b.c.my",None) if "c:\a" is on sys.path. + If no package found, will return ("my", "c:\a\b\c") + """ + path, fname = os.path.split(fileName) + path = origPath = win32ui.FullPath(path) + fname = os.path.splitext(fname)[0] + modBits = [] + newPathReturn = None + if not IsOnPythonPath(path): + # Module not directly on the search path - see if under a package. + while len(path) > 3: # ie 'C:\' + path, modBit = os.path.split(path) + modBits.append(modBit) + # If on path, _and_ existing package of that name loaded. + if ( + IsOnPythonPath(path) + and modBit in sys.modules + and ( + os.path.exists(os.path.join(path, modBit, "__init__.py")) + or os.path.exists(os.path.join(path, modBit, "__init__.pyc")) + or os.path.exists(os.path.join(path, modBit, "__init__.pyo")) + ) + ): + modBits.reverse() + return ".".join(modBits) + "." + fname, newPathReturn + # Not found - look a level higher + else: + newPathReturn = origPath + + return fname, newPathReturn + + +def GetActiveView(): + """Gets the edit control (eg, EditView) with the focus, or None""" + try: + childFrame, bIsMaximised = win32ui.GetMainFrame().MDIGetActive() + return childFrame.GetActiveView() + except win32ui.error: + return None + + +def GetActiveEditControl(): + view = GetActiveView() + if view is None: + return None + if hasattr(view, "SCIAddText"): # Is it a scintilla control? + return view + try: + return view.GetRichEditCtrl() + except AttributeError: + pass + try: + return view.GetEditCtrl() + except AttributeError: + pass + + +def GetActiveEditorDocument(): + """Returns the active editor document and view, or (None,None) if no + active document or its not an editor document. + """ + view = GetActiveView() + if view is None or isinstance(view, TreeView): + return (None, None) + doc = view.GetDocument() + if hasattr(doc, "MarkerAdd"): # Is it an Editor document? + return doc, view + return (None, None) + + +def GetActiveFileName(bAutoSave=1): + """Gets the file name for the active frame, saving it if necessary. + + Returns None if it cant be found, or raises KeyboardInterrupt. + """ + pathName = None + active = GetActiveView() + if active is None: + return None + try: + doc = active.GetDocument() + pathName = doc.GetPathName() + + if bAutoSave and ( + len(pathName) > 0 + or doc.GetTitle()[:8] == "Untitled" + or doc.GetTitle()[:6] == "Script" + ): # if not a special purpose window + if doc.IsModified(): + try: + doc.OnSaveDocument(pathName) + pathName = doc.GetPathName() + + # clear the linecache buffer + linecache.clearcache() + + except win32ui.error: + raise KeyboardInterrupt + + except (win32ui.error, AttributeError): + pass + if not pathName: + return None + return pathName + + +lastScript = "" +lastArgs = "" +lastDebuggingType = RS_DEBUGGER_NONE + + +def RunScript(defName=None, defArgs=None, bShowDialog=1, debuggingType=None): + global lastScript, lastArgs, lastDebuggingType + _debugger_stop_frame_ = 1 # Magic variable so the debugger will hide me! + + # Get the debugger - may be None! + debugger = GetDebugger() + + if defName is None: + try: + pathName = GetActiveFileName() + except KeyboardInterrupt: + return # User cancelled save. + else: + pathName = defName + if not pathName: + pathName = lastScript + if defArgs is None: + args = "" + if pathName == lastScript: + args = lastArgs + else: + args = defArgs + if debuggingType is None: + debuggingType = lastDebuggingType + + if not pathName or bShowDialog: + dlg = DlgRunScript(debugger is not None) + dlg["script"] = pathName + dlg["args"] = args + dlg["debuggingType"] = debuggingType + if dlg.DoModal() != win32con.IDOK: + return + script = dlg["script"] + args = dlg["args"] + debuggingType = dlg["debuggingType"] + if not script: + return + if debuggingType == RS_DEBUGGER_GO and debugger is not None: + # This may surprise users - they select "Run under debugger", but + # it appears not to! Only warn when they pick from the dialog! + # First - ensure the debugger is activated to pickup any break-points + # set in the editor. + try: + # Create the debugger, but _dont_ init the debugger GUI. + rd = debugger._GetCurrentDebugger() + except AttributeError: + rd = None + if rd is not None and len(rd.breaks) == 0: + msg = "There are no active break-points.\r\n\r\nSelecting this debug option without any\r\nbreak-points is unlikely to have the desired effect\r\nas the debugger is unlikely to be invoked..\r\n\r\nWould you like to step-through in the debugger instead?" + rc = win32ui.MessageBox( + msg, + win32ui.LoadString(win32ui.IDR_DEBUGGER), + win32con.MB_YESNOCANCEL | win32con.MB_ICONINFORMATION, + ) + if rc == win32con.IDCANCEL: + return + if rc == win32con.IDYES: + debuggingType = RS_DEBUGGER_STEP + + lastDebuggingType = debuggingType + lastScript = script + lastArgs = args + else: + script = pathName + + # try and open the script. + if ( + len(os.path.splitext(script)[1]) == 0 + ): # check if no extension supplied, and give one. + script = script + ".py" + # If no path specified, try and locate the file + path, fnameonly = os.path.split(script) + if len(path) == 0: + try: + os.stat(fnameonly) # See if it is OK as is... + script = fnameonly + except os.error: + fullScript = LocatePythonFile(script) + if fullScript is None: + win32ui.MessageBox("The file '%s' can not be located" % script) + return + script = fullScript + else: + path = win32ui.FullPath(path) + if not IsOnPythonPath(path): + sys.path.append(path) + + # py3k fun: If we use text mode to open the file, we get \r\n + # translated so Python allows the syntax (good!), but we get back + # text already decoded from the default encoding (bad!) and Python + # ignores any encoding decls (bad!). If we use binary mode we get + # the raw bytes and Python looks at the encoding (good!) but \r\n + # chars stay in place so Python throws a syntax error (bad!). + # So: so the binary thing and manually normalize \r\n. + try: + f = open(script, "rb") + except IOError as exc: + win32ui.MessageBox( + "The file could not be opened - %s (%d)" % (exc.strerror, exc.errno) + ) + return + + # Get the source-code - as above, normalize \r\n + code = f.read().replace(byte_crlf, byte_lf).replace(byte_cr, byte_lf) + byte_lf + + # Remember and hack sys.argv for the script. + oldArgv = sys.argv + sys.argv = ParseArgs(args) + sys.argv.insert(0, script) + # sys.path[0] is the path of the script + oldPath0 = sys.path[0] + newPath0 = os.path.split(script)[0] + if not oldPath0: # if sys.path[0] is empty + sys.path[0] = newPath0 + insertedPath0 = 0 + else: + sys.path.insert(0, newPath0) + insertedPath0 = 1 + bWorked = 0 + win32ui.DoWaitCursor(1) + base = os.path.split(script)[1] + # Allow windows to repaint before starting. + win32ui.PumpWaitingMessages() + win32ui.SetStatusText("Running script %s..." % base, 1) + exitCode = 0 + from pywin.framework import interact + + # Check the debugger flags + if debugger is None and (debuggingType != RS_DEBUGGER_NONE): + win32ui.MessageBox( + "No debugger is installed. Debugging options have been ignored!" + ) + debuggingType = RS_DEBUGGER_NONE + + # Get a code object - ignore the debugger for this, as it is probably a syntax error + # at this point + try: + codeObject = compile(code, script, "exec") + except: + # Almost certainly a syntax error! + _HandlePythonFailure("run script", script) + # No code object which to run/debug. + return + __main__.__file__ = script + try: + if debuggingType == RS_DEBUGGER_STEP: + debugger.run(codeObject, __main__.__dict__, start_stepping=1) + elif debuggingType == RS_DEBUGGER_GO: + debugger.run(codeObject, __main__.__dict__, start_stepping=0) + else: + # Post mortem or no debugging + exec(codeObject, __main__.__dict__) + bWorked = 1 + except bdb.BdbQuit: + # Dont print tracebacks when the debugger quit, but do print a message. + print("Debugging session cancelled.") + exitCode = 1 + bWorked = 1 + except SystemExit as code: + exitCode = code + bWorked = 1 + except KeyboardInterrupt: + # Consider this successful, as we dont want the debugger. + # (but we do want a traceback!) + if interact.edit and interact.edit.currentView: + interact.edit.currentView.EnsureNoPrompt() + traceback.print_exc() + if interact.edit and interact.edit.currentView: + interact.edit.currentView.AppendToPrompt([]) + bWorked = 1 + except: + if interact.edit and interact.edit.currentView: + interact.edit.currentView.EnsureNoPrompt() + traceback.print_exc() + if interact.edit and interact.edit.currentView: + interact.edit.currentView.AppendToPrompt([]) + if debuggingType == RS_DEBUGGER_PM: + debugger.pm() + del __main__.__file__ + sys.argv = oldArgv + if insertedPath0: + del sys.path[0] + else: + sys.path[0] = oldPath0 + f.close() + if bWorked: + win32ui.SetStatusText("Script '%s' returned exit code %s" % (script, exitCode)) + else: + win32ui.SetStatusText("Exception raised while running script %s" % base) + try: + sys.stdout.flush() + except AttributeError: + pass + + win32ui.DoWaitCursor(0) + + +def ImportFile(): + """This code looks for the current window, and determines if it can be imported. If not, + it will prompt for a file name, and allow it to be imported.""" + try: + pathName = GetActiveFileName() + except KeyboardInterrupt: + pathName = None + + if pathName is not None: + if os.path.splitext(pathName)[1].lower() not in (".py", ".pyw", ".pyx"): + pathName = None + + if pathName is None: + openFlags = win32con.OFN_OVERWRITEPROMPT | win32con.OFN_FILEMUSTEXIST + dlg = win32ui.CreateFileDialog( + 1, None, None, openFlags, "Python Scripts (*.py;*.pyw)|*.py;*.pyw;*.pyx||" + ) + dlg.SetOFNTitle("Import Script") + if dlg.DoModal() != win32con.IDOK: + return 0 + + pathName = dlg.GetPathName() + + # If already imported, dont look for package + path, modName = os.path.split(pathName) + modName, modExt = os.path.splitext(modName) + newPath = None + # note that some packages (*cough* email *cough*) use "lazy importers" + # meaning sys.modules can change as a side-effect of looking at + # module.__file__ - so we must take a copy (ie, items() in py2k, + # list(items()) in py3k) + for key, mod in list(sys.modules.items()): + if getattr(mod, "__file__", None): + fname = mod.__file__ + base, ext = os.path.splitext(fname) + if ext.lower() in (".pyo", ".pyc"): + ext = ".py" + fname = base + ext + if win32ui.ComparePath(fname, pathName): + modName = key + break + else: # for not broken + modName, newPath = GetPackageModuleName(pathName) + if newPath: + sys.path.append(newPath) + + if modName in sys.modules: + bNeedReload = 1 + what = "reload" + else: + what = "import" + bNeedReload = 0 + + win32ui.SetStatusText(what.capitalize() + "ing module...", 1) + win32ui.DoWaitCursor(1) + # win32ui.GetMainFrame().BeginWaitCursor() + + try: + # always do an import, as it is cheap if it's already loaded. This ensures + # it is in our name space. + codeObj = compile("import " + modName, "", "exec") + except SyntaxError: + win32ui.SetStatusText('Invalid filename for import: "' + modName + '"') + return + try: + exec(codeObj, __main__.__dict__) + mod = sys.modules.get(modName) + if bNeedReload: + from importlib import reload + + mod = reload(sys.modules[modName]) + win32ui.SetStatusText( + "Successfully " + + what + + "ed module '" + + modName + + "': %s" % getattr(mod, "__file__", "") + ) + except: + _HandlePythonFailure(what) + win32ui.DoWaitCursor(0) + + +def CheckFile(): + """This code looks for the current window, and gets Python to check it + without actually executing any code (ie, by compiling only) + """ + try: + pathName = GetActiveFileName() + except KeyboardInterrupt: + return + + what = "check" + win32ui.SetStatusText(what.capitalize() + "ing module...", 1) + win32ui.DoWaitCursor(1) + try: + f = open(pathName) + except IOError as details: + print("Cant open file '%s' - %s" % (pathName, details)) + return + try: + code = f.read() + "\n" + finally: + f.close() + try: + codeObj = compile(code, pathName, "exec") + if RunTabNanny(pathName): + win32ui.SetStatusText( + "Python and the TabNanny successfully checked the file '" + + os.path.basename(pathName) + + "'" + ) + except SyntaxError: + _HandlePythonFailure(what, pathName) + except: + traceback.print_exc() + _HandlePythonFailure(what) + win32ui.DoWaitCursor(0) + + +def RunTabNanny(filename): + import io as io + + tabnanny = FindTabNanny() + if tabnanny is None: + win32ui.MessageBox("The TabNanny is not around, so the children can run amok!") + return + + # Capture the tab-nanny output + newout = io.StringIO() + old_out = sys.stderr, sys.stdout + sys.stderr = sys.stdout = newout + try: + tabnanny.check(filename) + finally: + # Restore output + sys.stderr, sys.stdout = old_out + data = newout.getvalue() + if data: + try: + lineno = data.split()[1] + lineno = int(lineno) + _JumpToPosition(filename, lineno) + try: # Try and display whitespace + GetActiveEditControl().SCISetViewWS(1) + except: + pass + win32ui.SetStatusText("The TabNanny found trouble at line %d" % lineno) + except (IndexError, TypeError, ValueError): + print("The tab nanny complained, but I cant see where!") + print(data) + return 0 + return 1 + + +def _JumpToPosition(fileName, lineno, col=1): + JumpToDocument(fileName, lineno, col) + + +def JumpToDocument(fileName, lineno=0, col=1, nChars=0, bScrollToTop=0): + # Jump to the position in a file. + # If lineno is <= 0, dont move the position - just open/restore. + # if nChars > 0, select that many characters. + # if bScrollToTop, the specified line will be moved to the top of the window + # (eg, bScrollToTop should be false when jumping to an error line to retain the + # context, but true when jumping to a method defn, where we want the full body. + # Return the view which is editing the file, or None on error. + doc = win32ui.GetApp().OpenDocumentFile(fileName) + if doc is None: + return None + frame = doc.GetFirstView().GetParentFrame() + try: + view = frame.GetEditorView() + if frame.GetActiveView() != view: + frame.SetActiveView(view) + frame.AutoRestore() + except AttributeError: # Not an editor frame?? + view = doc.GetFirstView() + if lineno > 0: + charNo = view.LineIndex(lineno - 1) + start = charNo + col - 1 + size = view.GetTextLength() + try: + view.EnsureCharsVisible(charNo) + except AttributeError: + print("Doesnt appear to be one of our views?") + view.SetSel(min(start, size), min(start + nChars, size)) + if bScrollToTop: + curTop = view.GetFirstVisibleLine() + nScroll = (lineno - 1) - curTop + view.LineScroll(nScroll, 0) + view.SetFocus() + return view + + +def _HandlePythonFailure(what, syntaxErrorPathName=None): + typ, details, tb = sys.exc_info() + if isinstance(details, SyntaxError): + try: + msg, (fileName, line, col, text) = details + if (not fileName or fileName == "") and syntaxErrorPathName: + fileName = syntaxErrorPathName + _JumpToPosition(fileName, line, col) + except (TypeError, ValueError): + msg = str(details) + win32ui.SetStatusText("Failed to " + what + " - syntax error - %s" % msg) + else: + traceback.print_exc() + win32ui.SetStatusText("Failed to " + what + " - " + str(details)) + tb = None # Clean up a cycle. + + +# Find the Python TabNanny in either the standard library or the Python Tools/Scripts directory. +def FindTabNanny(): + try: + return __import__("tabnanny") + except ImportError: + pass + # OK - not in the standard library - go looking. + filename = "tabnanny.py" + try: + path = win32api.RegQueryValue( + win32con.HKEY_LOCAL_MACHINE, + "SOFTWARE\\Python\\PythonCore\\%s\\InstallPath" % (sys.winver), + ) + except win32api.error: + print("WARNING - The Python registry does not have an 'InstallPath' setting") + print(" The file '%s' can not be located" % (filename)) + return None + fname = os.path.join(path, "Tools\\Scripts\\%s" % filename) + try: + os.stat(fname) + except os.error: + print( + "WARNING - The file '%s' can not be located in path '%s'" % (filename, path) + ) + return None + + tabnannyhome, tabnannybase = os.path.split(fname) + tabnannybase = os.path.splitext(tabnannybase)[0] + # Put tab nanny at the top of the path. + sys.path.insert(0, tabnannyhome) + try: + return __import__(tabnannybase) + finally: + # remove the tab-nanny from the path + del sys.path[0] + + +def LocatePythonFile(fileName, bBrowseIfDir=1): + "Given a file name, return a fully qualified file name, or None" + # first look for the exact file as specified + if not os.path.isfile(fileName): + # Go looking! + baseName = fileName + for path in sys.path: + fileName = os.path.abspath(os.path.join(path, baseName)) + if os.path.isdir(fileName): + if bBrowseIfDir: + d = win32ui.CreateFileDialog( + 1, "*.py", None, 0, "Python Files (*.py)|*.py|All files|*.*" + ) + d.SetOFNInitialDir(fileName) + rc = d.DoModal() + if rc == win32con.IDOK: + fileName = d.GetPathName() + break + else: + return None + else: + fileName = fileName + ".py" + if os.path.isfile(fileName): + break # Found it! + + else: # for not broken out of + return None + return win32ui.FullPath(fileName) diff --git a/myenv/Lib/site-packages/pythonwin/pywin/framework/sgrepmdi.py b/myenv/Lib/site-packages/pythonwin/pywin/framework/sgrepmdi.py new file mode 100644 index 000000000..b7d938bbb --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/framework/sgrepmdi.py @@ -0,0 +1,758 @@ +# SGrepMDI is by Gordon McMillan (gmcm@hypernet.com) +# It does basically what Find In Files does in MSVC with a couple enhancements. +# - It saves any directories in the app's ini file (if you want to get rid +# of them you'll have to edit the file) +# - "Directories" can be directories, +# - semicolon separated lists of "directories", +# - environment variables that evaluate to "directories", +# - registry path names that evaluate to "directories", +# - all of which is recursive, so you can mix them all up. +# - It is MDI, so you can 'nest' greps and return to earlier ones, +# (ie, have multiple results open at the same time) +# - Like FIF, double clicking a line opens an editor and takes you to the line. +# - You can highlight text, right click and start a new grep with the selected +# text as search pattern and same directories etc as before. +# - You can save grep parameters (so you don't lose your hardearned pattern) +# from File|Save +# - You can save grep results by right clicking in the result window. +# Hats off to Mark Hammond for providing an environment where I could cobble +# something like this together in a couple evenings! + +import glob +import os +import re + +import win32api +import win32con +import win32ui +from pywin.mfc import dialog, docview, window + +from . import scriptutils + + +def getsubdirs(d): + dlist = [] + flist = glob.glob(d + "\\*") + for f in flist: + if os.path.isdir(f): + dlist.append(f) + dlist = dlist + getsubdirs(f) + return dlist + + +class dirpath: + def __init__(self, str, recurse=0): + dp = str.split(";") + dirs = {} + for d in dp: + if os.path.isdir(d): + d = d.lower() + if d not in dirs: + dirs[d] = None + if recurse: + subdirs = getsubdirs(d) + for sd in subdirs: + sd = sd.lower() + if sd not in dirs: + dirs[sd] = None + elif os.path.isfile(d): + pass + else: + x = None + if d in os.environ: + x = dirpath(os.environ[d]) + elif d[:5] == "HKEY_": + keystr = d.split("\\") + try: + root = eval("win32con." + keystr[0]) + except: + win32ui.MessageBox( + "Can't interpret registry key name '%s'" % keystr[0] + ) + try: + subkey = "\\".join(keystr[1:]) + val = win32api.RegQueryValue(root, subkey) + if val: + x = dirpath(val) + else: + win32ui.MessageBox( + "Registry path '%s' did not return a path entry" % d + ) + except: + win32ui.MessageBox( + "Can't interpret registry key value: %s" % keystr[1:] + ) + else: + win32ui.MessageBox("Directory '%s' not found" % d) + if x: + for xd in x: + if xd not in dirs: + dirs[xd] = None + if recurse: + subdirs = getsubdirs(xd) + for sd in subdirs: + sd = sd.lower() + if sd not in dirs: + dirs[sd] = None + self.dirs = [] + for d in list(dirs.keys()): + self.dirs.append(d) + + def __getitem__(self, key): + return self.dirs[key] + + def __len__(self): + return len(self.dirs) + + def __setitem__(self, key, value): + self.dirs[key] = value + + def __delitem__(self, key): + del self.dirs[key] + + def __getslice__(self, lo, hi): + return self.dirs[lo:hi] + + def __setslice__(self, lo, hi, seq): + self.dirs[lo:hi] = seq + + def __delslice__(self, lo, hi): + del self.dirs[lo:hi] + + def __add__(self, other): + if type(other) == type(self) or type(other) == type([]): + return self.dirs + other.dirs + + def __radd__(self, other): + if type(other) == type(self) or type(other) == type([]): + return other.dirs + self.dirs + + +# Group(1) is the filename, group(2) is the lineno. +# regexGrepResult=regex.compile("^\\([a-zA-Z]:.*\\)(\\([0-9]+\\))") + +regexGrep = re.compile(r"^([a-zA-Z]:[^(]*)\(([0-9]+)\)") + +# these are the atom numbers defined by Windows for basic dialog controls + +BUTTON = 0x80 +EDIT = 0x81 +STATIC = 0x82 +LISTBOX = 0x83 +SCROLLBAR = 0x84 +COMBOBOX = 0x85 + + +class GrepTemplate(docview.RichEditDocTemplate): + def __init__(self): + docview.RichEditDocTemplate.__init__( + self, win32ui.IDR_TEXTTYPE, GrepDocument, GrepFrame, GrepView + ) + self.SetDocStrings("\nGrep\nGrep\nGrep params (*.grep)\n.grep\n\n\n") + win32ui.GetApp().AddDocTemplate(self) + self.docparams = None + + def MatchDocType(self, fileName, fileType): + doc = self.FindOpenDocument(fileName) + if doc: + return doc + ext = os.path.splitext(fileName)[1].lower() + if ext == ".grep": + return win32ui.CDocTemplate_Confidence_yesAttemptNative + return win32ui.CDocTemplate_Confidence_noAttempt + + def setParams(self, params): + self.docparams = params + + def readParams(self): + tmp = self.docparams + self.docparams = None + return tmp + + +class GrepFrame(window.MDIChildWnd): + # The template and doc params will one day be removed. + def __init__(self, wnd=None): + window.MDIChildWnd.__init__(self, wnd) + + +class GrepDocument(docview.RichEditDoc): + def __init__(self, template): + docview.RichEditDoc.__init__(self, template) + self.dirpattern = "" + self.filpattern = "" + self.greppattern = "" + self.casesensitive = 1 + self.recurse = 1 + self.verbose = 0 + + def OnOpenDocument(self, fnm): + # this bizarre stuff with params is so right clicking in a result window + # and starting a new grep can communicate the default parameters to the + # new grep. + try: + params = open(fnm, "r").read() + except: + params = None + self.setInitParams(params) + return self.OnNewDocument() + + def OnCloseDocument(self): + try: + win32ui.GetApp().DeleteIdleHandler(self.SearchFile) + except: + pass + return self._obj_.OnCloseDocument() + + def saveInitParams(self): + # Only save the flags, not the text boxes. + paramstr = "\t%s\t\t%d\t%d" % ( + self.filpattern, + self.casesensitive, + self.recurse, + ) + win32ui.WriteProfileVal("Grep", "Params", paramstr) + + def setInitParams(self, paramstr): + if paramstr is None: + paramstr = win32ui.GetProfileVal("Grep", "Params", "\t\t\t1\t0\t0") + params = paramstr.split("\t") + if len(params) < 3: + params = params + [""] * (3 - len(params)) + if len(params) < 6: + params = params + [0] * (6 - len(params)) + self.dirpattern = params[0] + self.filpattern = params[1] + self.greppattern = params[2] + self.casesensitive = int(params[3]) + self.recurse = int(params[4]) + self.verbose = int(params[5]) + # setup some reasonable defaults. + if not self.dirpattern: + try: + editor = win32ui.GetMainFrame().MDIGetActive()[0].GetEditorView() + self.dirpattern = os.path.abspath( + os.path.dirname(editor.GetDocument().GetPathName()) + ) + except (AttributeError, win32ui.error): + self.dirpattern = os.getcwd() + if not self.filpattern: + self.filpattern = "*.py" + + def OnNewDocument(self): + if self.dirpattern == "": + self.setInitParams(greptemplate.readParams()) + d = GrepDialog( + self.dirpattern, + self.filpattern, + self.greppattern, + self.casesensitive, + self.recurse, + self.verbose, + ) + if d.DoModal() == win32con.IDOK: + self.dirpattern = d["dirpattern"] + self.filpattern = d["filpattern"] + self.greppattern = d["greppattern"] + self.casesensitive = d["casesensitive"] + self.recurse = d["recursive"] + self.verbose = d["verbose"] + self.doSearch() + self.saveInitParams() + return 1 + return 0 # cancelled - return zero to stop frame creation. + + def doSearch(self): + self.dp = dirpath(self.dirpattern, self.recurse) + self.SetTitle("Grep for %s in %s" % (self.greppattern, self.filpattern)) + # self.text = [] + self.GetFirstView().Append("#Search " + self.dirpattern + "\n") + if self.verbose: + self.GetFirstView().Append("# =" + repr(self.dp.dirs) + "\n") + self.GetFirstView().Append("# Files " + self.filpattern + "\n") + self.GetFirstView().Append("# For " + self.greppattern + "\n") + self.fplist = self.filpattern.split(";") + if self.casesensitive: + self.pat = re.compile(self.greppattern) + else: + self.pat = re.compile(self.greppattern, re.IGNORECASE) + win32ui.SetStatusText("Searching. Please wait...", 0) + self.dpndx = self.fpndx = 0 + self.fndx = -1 + if not self.dp: + self.GetFirstView().Append( + "# ERROR: '%s' does not resolve to any search locations" + % self.dirpattern + ) + self.SetModifiedFlag(0) + else: + self.flist = glob.glob(self.dp[0] + "\\" + self.fplist[0]) + win32ui.GetApp().AddIdleHandler(self.SearchFile) + + def SearchFile(self, handler, count): + self.fndx = self.fndx + 1 + if self.fndx < len(self.flist): + f = self.flist[self.fndx] + if self.verbose: + self.GetFirstView().Append("# .." + f + "\n") + # Directories may match the file type pattern, and files may be removed + # while grep is running + if os.path.isfile(f): + win32ui.SetStatusText("Searching " + f, 0) + lines = open(f, "r").readlines() + for i in range(len(lines)): + line = lines[i] + if self.pat.search(line) != None: + self.GetFirstView().Append(f + "(" + repr(i + 1) + ") " + line) + else: + self.fndx = -1 + self.fpndx = self.fpndx + 1 + if self.fpndx < len(self.fplist): + self.flist = glob.glob( + self.dp[self.dpndx] + "\\" + self.fplist[self.fpndx] + ) + else: + self.fpndx = 0 + self.dpndx = self.dpndx + 1 + if self.dpndx < len(self.dp): + self.flist = glob.glob( + self.dp[self.dpndx] + "\\" + self.fplist[self.fpndx] + ) + else: + win32ui.SetStatusText("Search complete.", 0) + self.SetModifiedFlag(0) # default to not modified. + try: + win32ui.GetApp().DeleteIdleHandler(self.SearchFile) + except: + pass + return 0 + return 1 + + def GetParams(self): + return ( + self.dirpattern + + "\t" + + self.filpattern + + "\t" + + self.greppattern + + "\t" + + repr(self.casesensitive) + + "\t" + + repr(self.recurse) + + "\t" + + repr(self.verbose) + ) + + def OnSaveDocument(self, filename): + # print 'OnSaveDocument() filename=',filename + savefile = open(filename, "wb") + txt = self.GetParams() + "\n" + # print 'writing',txt + savefile.write(txt) + savefile.close() + self.SetModifiedFlag(0) + return 1 + + +ID_OPEN_FILE = 0xE400 +ID_GREP = 0xE401 +ID_SAVERESULTS = 0x402 +ID_TRYAGAIN = 0x403 + + +class GrepView(docview.RichEditView): + def __init__(self, doc): + docview.RichEditView.__init__(self, doc) + self.SetWordWrap(win32ui.CRichEditView_WrapNone) + self.HookHandlers() + + def OnInitialUpdate(self): + rc = self._obj_.OnInitialUpdate() + format = (-402653169, 0, 200, 0, 0, 0, 49, "Courier New") + self.SetDefaultCharFormat(format) + return rc + + def HookHandlers(self): + self.HookMessage(self.OnRClick, win32con.WM_RBUTTONDOWN) + self.HookCommand(self.OnCmdOpenFile, ID_OPEN_FILE) + self.HookCommand(self.OnCmdGrep, ID_GREP) + self.HookCommand(self.OnCmdSave, ID_SAVERESULTS) + self.HookCommand(self.OnTryAgain, ID_TRYAGAIN) + self.HookMessage(self.OnLDblClick, win32con.WM_LBUTTONDBLCLK) + + def OnLDblClick(self, params): + line = self.GetLine() + regexGrepResult = regexGrep.match(line) + if regexGrepResult: + fname = regexGrepResult.group(1) + line = int(regexGrepResult.group(2)) + scriptutils.JumpToDocument(fname, line) + return 0 # dont pass on + return 1 # pass it on by default. + + def OnRClick(self, params): + menu = win32ui.CreatePopupMenu() + flags = win32con.MF_STRING | win32con.MF_ENABLED + lineno = self._obj_.LineFromChar(-1) # selection or current line + line = self._obj_.GetLine(lineno) + regexGrepResult = regexGrep.match(line) + if regexGrepResult: + self.fnm = regexGrepResult.group(1) + self.lnnum = int(regexGrepResult.group(2)) + menu.AppendMenu(flags, ID_OPEN_FILE, "&Open " + self.fnm) + menu.AppendMenu(win32con.MF_SEPARATOR) + menu.AppendMenu(flags, ID_TRYAGAIN, "&Try Again") + charstart, charend = self._obj_.GetSel() + if charstart != charend: + linestart = self._obj_.LineIndex(lineno) + self.sel = line[charstart - linestart : charend - linestart] + menu.AppendMenu(flags, ID_GREP, "&Grep for " + self.sel) + menu.AppendMenu(win32con.MF_SEPARATOR) + menu.AppendMenu(flags, win32ui.ID_EDIT_CUT, "Cu&t") + menu.AppendMenu(flags, win32ui.ID_EDIT_COPY, "&Copy") + menu.AppendMenu(flags, win32ui.ID_EDIT_PASTE, "&Paste") + menu.AppendMenu(flags, win32con.MF_SEPARATOR) + menu.AppendMenu(flags, win32ui.ID_EDIT_SELECT_ALL, "&Select all") + menu.AppendMenu(flags, win32con.MF_SEPARATOR) + menu.AppendMenu(flags, ID_SAVERESULTS, "Sa&ve results") + menu.TrackPopupMenu(params[5]) + return 0 + + def OnCmdOpenFile(self, cmd, code): + doc = win32ui.GetApp().OpenDocumentFile(self.fnm) + if doc: + vw = doc.GetFirstView() + # hope you have an editor that implements GotoLine()! + try: + vw.GotoLine(int(self.lnnum)) + except: + pass + return 0 + + def OnCmdGrep(self, cmd, code): + if code != 0: + return 1 + curparamsstr = self.GetDocument().GetParams() + params = curparamsstr.split("\t") + params[2] = self.sel + greptemplate.setParams("\t".join(params)) + greptemplate.OpenDocumentFile() + return 0 + + def OnTryAgain(self, cmd, code): + if code != 0: + return 1 + greptemplate.setParams(self.GetDocument().GetParams()) + greptemplate.OpenDocumentFile() + return 0 + + def OnCmdSave(self, cmd, code): + if code != 0: + return 1 + flags = win32con.OFN_OVERWRITEPROMPT + dlg = win32ui.CreateFileDialog( + 0, None, None, flags, "Text Files (*.txt)|*.txt||", self + ) + dlg.SetOFNTitle("Save Results As") + if dlg.DoModal() == win32con.IDOK: + pn = dlg.GetPathName() + self._obj_.SaveTextFile(pn) + return 0 + + def Append(self, strng): + numlines = self.GetLineCount() + endpos = self.LineIndex(numlines - 1) + len(self.GetLine(numlines - 1)) + self.SetSel(endpos, endpos) + self.ReplaceSel(strng) + + +class GrepDialog(dialog.Dialog): + def __init__(self, dp, fp, gp, cs, r, v): + style = ( + win32con.DS_MODALFRAME + | win32con.WS_POPUP + | win32con.WS_VISIBLE + | win32con.WS_CAPTION + | win32con.WS_SYSMENU + | win32con.DS_SETFONT + ) + CS = win32con.WS_CHILD | win32con.WS_VISIBLE + tmp = [ + ["Grep", (0, 0, 210, 90), style, None, (8, "MS Sans Serif")], + ] + tmp.append([STATIC, "Grep For:", -1, (7, 7, 50, 9), CS]) + tmp.append( + [ + EDIT, + gp, + 101, + (52, 7, 144, 11), + CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER, + ] + ) + tmp.append([STATIC, "Directories:", -1, (7, 20, 50, 9), CS]) + tmp.append( + [ + EDIT, + dp, + 102, + (52, 20, 128, 11), + CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER, + ] + ) + tmp.append( + [ + BUTTON, + "...", + 110, + (182, 20, 16, 11), + CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP, + ] + ) + tmp.append([STATIC, "File types:", -1, (7, 33, 50, 9), CS]) + tmp.append( + [ + EDIT, + fp, + 103, + (52, 33, 128, 11), + CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER, + ] + ) + tmp.append( + [ + BUTTON, + "...", + 111, + (182, 33, 16, 11), + CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP, + ] + ) + tmp.append( + [ + BUTTON, + "Case sensitive", + 104, + (7, 45, 72, 9), + CS + | win32con.BS_AUTOCHECKBOX + | win32con.BS_LEFTTEXT + | win32con.WS_TABSTOP, + ] + ) + tmp.append( + [ + BUTTON, + "Subdirectories", + 105, + (7, 56, 72, 9), + CS + | win32con.BS_AUTOCHECKBOX + | win32con.BS_LEFTTEXT + | win32con.WS_TABSTOP, + ] + ) + tmp.append( + [ + BUTTON, + "Verbose", + 106, + (7, 67, 72, 9), + CS + | win32con.BS_AUTOCHECKBOX + | win32con.BS_LEFTTEXT + | win32con.WS_TABSTOP, + ] + ) + tmp.append( + [ + BUTTON, + "OK", + win32con.IDOK, + (166, 53, 32, 12), + CS | win32con.BS_DEFPUSHBUTTON | win32con.WS_TABSTOP, + ] + ) + tmp.append( + [ + BUTTON, + "Cancel", + win32con.IDCANCEL, + (166, 67, 32, 12), + CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP, + ] + ) + dialog.Dialog.__init__(self, tmp) + self.AddDDX(101, "greppattern") + self.AddDDX(102, "dirpattern") + self.AddDDX(103, "filpattern") + self.AddDDX(104, "casesensitive") + self.AddDDX(105, "recursive") + self.AddDDX(106, "verbose") + self._obj_.data["greppattern"] = gp + self._obj_.data["dirpattern"] = dp + self._obj_.data["filpattern"] = fp + self._obj_.data["casesensitive"] = cs + self._obj_.data["recursive"] = r + self._obj_.data["verbose"] = v + self.HookCommand(self.OnMoreDirectories, 110) + self.HookCommand(self.OnMoreFiles, 111) + + def OnMoreDirectories(self, cmd, code): + if code != 0: + return 1 + self.getMore("Grep\\Directories", "dirpattern") + + def OnMoreFiles(self, cmd, code): + if code != 0: + return 1 + self.getMore("Grep\\File Types", "filpattern") + + def getMore(self, section, key): + self.UpdateData(1) + # get the items out of the ini file + ini = win32ui.GetProfileFileName() + secitems = win32api.GetProfileSection(section, ini) + items = [] + for secitem in secitems: + items.append(secitem.split("=")[1]) + dlg = GrepParamsDialog(items) + if dlg.DoModal() == win32con.IDOK: + itemstr = ";".join(dlg.getItems()) + self._obj_.data[key] = itemstr + # update the ini file with dlg.getNew() + i = 0 + newitems = dlg.getNew() + if newitems: + items = items + newitems + for item in items: + win32api.WriteProfileVal(section, repr(i), item, ini) + i = i + 1 + self.UpdateData(0) + + def OnOK(self): + self.UpdateData(1) + for id, name in ( + (101, "greppattern"), + (102, "dirpattern"), + (103, "filpattern"), + ): + if not self[name]: + self.GetDlgItem(id).SetFocus() + win32api.MessageBeep() + win32ui.SetStatusText("Please enter a value") + return + self._obj_.OnOK() + + +class GrepParamsDialog(dialog.Dialog): + def __init__(self, items): + self.items = items + self.newitems = [] + style = ( + win32con.DS_MODALFRAME + | win32con.WS_POPUP + | win32con.WS_VISIBLE + | win32con.WS_CAPTION + | win32con.WS_SYSMENU + | win32con.DS_SETFONT + ) + CS = win32con.WS_CHILD | win32con.WS_VISIBLE + tmp = [ + ["Grep Parameters", (0, 0, 205, 100), style, None, (8, "MS Sans Serif")], + ] + tmp.append( + [ + LISTBOX, + "", + 107, + (7, 7, 150, 72), + CS + | win32con.LBS_MULTIPLESEL + | win32con.LBS_STANDARD + | win32con.LBS_HASSTRINGS + | win32con.WS_TABSTOP + | win32con.LBS_NOTIFY, + ] + ) + tmp.append( + [ + BUTTON, + "OK", + win32con.IDOK, + (167, 7, 32, 12), + CS | win32con.BS_DEFPUSHBUTTON | win32con.WS_TABSTOP, + ] + ) + tmp.append( + [ + BUTTON, + "Cancel", + win32con.IDCANCEL, + (167, 23, 32, 12), + CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP, + ] + ) + tmp.append([STATIC, "New:", -1, (2, 83, 15, 12), CS]) + tmp.append( + [ + EDIT, + "", + 108, + (18, 83, 139, 12), + CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER, + ] + ) + tmp.append( + [ + BUTTON, + "Add", + 109, + (167, 83, 32, 12), + CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP, + ] + ) + dialog.Dialog.__init__(self, tmp) + self.HookCommand(self.OnAddItem, 109) + self.HookCommand(self.OnListDoubleClick, 107) + + def OnInitDialog(self): + lb = self.GetDlgItem(107) + for item in self.items: + lb.AddString(item) + return self._obj_.OnInitDialog() + + def OnAddItem(self, cmd, code): + if code != 0: + return 1 + eb = self.GetDlgItem(108) + item = eb.GetLine(0) + self.newitems.append(item) + lb = self.GetDlgItem(107) + i = lb.AddString(item) + lb.SetSel(i, 1) + return 1 + + def OnListDoubleClick(self, cmd, code): + if code == win32con.LBN_DBLCLK: + self.OnOK() + return 1 + + def OnOK(self): + lb = self.GetDlgItem(107) + self.selections = lb.GetSelTextItems() + self._obj_.OnOK() + + def getItems(self): + return self.selections + + def getNew(self): + return self.newitems + + +try: + win32ui.GetApp().RemoveDocTemplate(greptemplate) +except NameError: + pass + +greptemplate = GrepTemplate() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/framework/startup.py b/myenv/Lib/site-packages/pythonwin/pywin/framework/startup.py new file mode 100644 index 000000000..4a7323225 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/framework/startup.py @@ -0,0 +1,80 @@ +# startup.py +# +"The main application startup code for PythonWin." + +# +# This does the basic command line handling. + +# Keep this as short as possible, cos error output is only redirected if +# this runs OK. Errors in imported modules are much better - the messages go somewhere (not any more :-) + +import os +import sys + +import win32api +import win32ui + +if not sys.argv: + # Initialize sys.argv from commandline. When sys.argv is empty list ( + # different from [''] meaning "no cmd line arguments" ), then C + # bootstrapping or another method of invocation failed to initialize + # sys.argv and it will be done here. ( This was a workaround for a bug in + # win32ui but is retained for other situations. ) + argv = win32api.CommandLineToArgv(win32api.GetCommandLine()) + sys.argv = argv[1:] + if os.getcwd() not in sys.path and "." not in sys.path: + sys.path.insert(0, os.getcwd()) + +# You may wish to redirect error output somewhere useful if you have startup errors. +# eg, 'import win32traceutil' will do this for you. +# import win32traceutil # Just uncomment this line to see error output! + +# An old class I used to use - generally only useful if Pythonwin is running under MSVC +# class DebugOutput: +# softspace=1 +# def write(self,message): +# win32ui.OutputDebug(message) +# sys.stderr=sys.stdout=DebugOutput() + +# To fix a problem with Pythonwin when started from the Pythonwin directory, +# we update the pywin path to ensure it is absolute. +# If it is indeed relative, it will be relative to our current directory. +# If its already absolute, then this will have no affect. +import pywin +import pywin.framework + +pywin.__path__[0] = win32ui.FullPath(pywin.__path__[0]) +pywin.framework.__path__[0] = win32ui.FullPath(pywin.framework.__path__[0]) + +# make a few wierd sys values. This is so later we can clobber sys.argv to trick +# scripts when running under a GUI environment. + +moduleName = "pywin.framework.intpyapp" +sys.appargvoffset = 0 +sys.appargv = sys.argv[:] +# Must check for /app param here. +if len(sys.argv) >= 2 and sys.argv[0].lower() in ("/app", "-app"): + from . import cmdline + + moduleName = cmdline.FixArgFileName(sys.argv[1]) + sys.appargvoffset = 2 + newargv = sys.argv[sys.appargvoffset :] + # newargv.insert(0, sys.argv[0]) + sys.argv = newargv + +# Import the application module. +__import__(moduleName) + +try: + win32ui.GetApp()._obj_ + # This worked - an app already exists - do nothing more +except (AttributeError, win32ui.error): + # This means either no app object exists at all, or the one + # that does exist does not have a Python class (ie, was created + # by the host .EXE). In this case, we do the "old style" init... + from . import app + + if app.AppBuilder is None: + raise TypeError("No application object has been registered") + + app.App = app.AppBuilder() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/framework/stdin.py b/myenv/Lib/site-packages/pythonwin/pywin/framework/stdin.py new file mode 100644 index 000000000..2f3adcb6b --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/framework/stdin.py @@ -0,0 +1,176 @@ +# Copyright (c) 2000 David Abrahams. Permission to copy, use, modify, sell +# and distribute this software is granted provided this copyright +# notice appears in all copies. This software is provided "as is" without +# express or implied warranty, and with no claim as to its suitability for +# any purpose. +"""Provides a class Stdin which can be used to emulate the regular old +sys.stdin for the PythonWin interactive window. Right now it just pops +up a raw_input() dialog. With luck, someone will integrate it into the +actual PythonWin interactive window someday. + +WARNING: Importing this file automatically replaces sys.stdin with an +instance of Stdin (below). This is useful because you can just open +Stdin.py in PythonWin and hit the import button to get it set up right +if you don't feel like changing PythonWin's source. To put things back +the way they were, simply use this magic incantation: + import sys + sys.stdin = sys.stdin.real_file +""" +import sys + +try: + get_input_line = raw_input # py2x +except NameError: + get_input_line = input # py3k + + +class Stdin: + def __init__(self): + self.real_file = sys.stdin # NOTE: Likely to be None in py3k + self.buffer = "" + self.closed = False + + def __getattr__(self, name): + """Forward most functions to the real sys.stdin for absolute realism.""" + if self.real_file is None: + raise AttributeError(name) + return getattr(self.real_file, name) + + def isatty(self): + """Return 1 if the file is connected to a tty(-like) device, else 0.""" + return 1 + + def read(self, size=-1): + """Read at most size bytes from the file (less if the read + hits EOF or no more data is immediately available on a pipe, + tty or similar device). If the size argument is negative or + omitted, read all data until EOF is reached. The bytes are + returned as a string object. An empty string is returned when + EOF is encountered immediately. (For certain files, like ttys, + it makes sense to continue reading after an EOF is hit.)""" + result_size = self.__get_lines(size) + return self.__extract_from_buffer(result_size) + + def readline(self, size=-1): + """Read one entire line from the file. A trailing newline + character is kept in the string2.6 (but may be absent when a file ends + with an incomplete line). If the size argument is present and + non-negative, it is a maximum byte count (including the trailing + newline) and an incomplete line may be returned. An empty string is + returned when EOF is hit immediately. Note: unlike stdio's fgets(), + the returned string contains null characters ('\0') if they occurred + in the input. + """ + maximum_result_size = self.__get_lines(size, lambda buffer: "\n" in buffer) + + if "\n" in self.buffer[:maximum_result_size]: + result_size = self.buffer.find("\n", 0, maximum_result_size) + 1 + assert result_size > 0 + else: + result_size = maximum_result_size + + return self.__extract_from_buffer(result_size) + + def __extract_from_buffer(self, character_count): + """Remove the first character_count characters from the internal buffer and + return them. + """ + result = self.buffer[:character_count] + self.buffer = self.buffer[character_count:] + return result + + def __get_lines(self, desired_size, done_reading=lambda buffer: False): + """Keep adding lines to our internal buffer until done_reading(self.buffer) + is true or EOF has been reached or we have desired_size bytes in the buffer. + If desired_size < 0, we are never satisfied until we reach EOF. If done_reading + is not supplied, it is not consulted. + + If desired_size < 0, returns the length of the internal buffer. Otherwise, + returns desired_size. + """ + while not done_reading(self.buffer) and ( + desired_size < 0 or len(self.buffer) < desired_size + ): + try: + self.__get_line() + except ( + EOFError, + KeyboardInterrupt, + ): # deal with cancellation of get_input_line dialog + desired_size = len(self.buffer) # Be satisfied! + + if desired_size < 0: + return len(self.buffer) + else: + return desired_size + + def __get_line(self): + """Grab one line from get_input_line() and append it to the buffer.""" + line = get_input_line() + print(">>>", line) # echo input to console + self.buffer = self.buffer + line + "\n" + + def readlines(self, *sizehint): + """Read until EOF using readline() and return a list containing the lines + thus read. If the optional sizehint argument is present, instead of + reading up to EOF, whole lines totalling approximately sizehint bytes + (possibly after rounding up to an internal buffer size) are read. + """ + result = [] + total_read = 0 + while sizehint == () or total_read < sizehint[0]: + line = self.readline() + if line == "": + break + total_read = total_read + len(line) + result.append(line) + return result + + +if __name__ == "__main__": + test_input = r"""this is some test +input that I am hoping +~ +will be very instructive +and when I am done +I will have tested everything. +Twelve and twenty blackbirds +baked in a pie. Patty cake +patty cake so am I. +~ +Thirty-five niggling idiots! +Sell you soul to the devil, baby +""" + + def fake_raw_input(prompt=None): + """Replacement for raw_input() which pulls lines out of global test_input. + For testing only! + """ + global test_input + if "\n" not in test_input: + end_of_line_pos = len(test_input) + else: + end_of_line_pos = test_input.find("\n") + result = test_input[:end_of_line_pos] + test_input = test_input[end_of_line_pos + 1 :] + if len(result) == 0 or result[0] == "~": + raise EOFError() + return result + + get_input_line = fake_raw_input + + # Some completely inadequate tests, just to make sure the code's not totally broken + try: + x = Stdin() + print(x.read()) + print(x.readline()) + print(x.read(12)) + print(x.readline(47)) + print(x.readline(3)) + print(x.readlines()) + finally: + get_input_line = raw_input +else: + import sys + + sys.stdin = Stdin() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/framework/toolmenu.py b/myenv/Lib/site-packages/pythonwin/pywin/framework/toolmenu.py new file mode 100644 index 000000000..3f739e09e --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/framework/toolmenu.py @@ -0,0 +1,284 @@ +# toolmenu.py + +import sys + +import win32api +import win32con +import win32ui + +from . import app + +tools = {} +idPos = 100 + +# The default items should no tools menu exist in the INI file. +defaultToolMenuItems = [ + ("Browser", "win32ui.GetApp().OnViewBrowse(0,0)"), + ( + "Browse PythonPath", + "from pywin.tools import browseProjects;browseProjects.Browse()", + ), + ("Edit Python Path", "from pywin.tools import regedit;regedit.EditRegistry()"), + ("COM Makepy utility", "from win32com.client import makepy;makepy.main()"), + ( + "COM Browser", + "from win32com.client import combrowse;combrowse.main(modal=False)", + ), + ( + "Trace Collector Debugging tool", + "from pywin.tools import TraceCollector;TraceCollector.MakeOutputWindow()", + ), +] + + +def LoadToolMenuItems(): + # Load from the registry. + items = [] + lookNo = 1 + while 1: + menu = win32ui.GetProfileVal("Tools Menu\\%s" % lookNo, "", "") + if menu == "": + break + cmd = win32ui.GetProfileVal("Tools Menu\\%s" % lookNo, "Command", "") + items.append((menu, cmd)) + lookNo = lookNo + 1 + + if len(items) == 0: + items = defaultToolMenuItems + return items + + +def WriteToolMenuItems(items): + # Items is a list of (menu, command) + # Delete the entire registry tree. + try: + mainKey = win32ui.GetAppRegistryKey() + toolKey = win32api.RegOpenKey(mainKey, "Tools Menu") + except win32ui.error: + toolKey = None + if toolKey is not None: + while 1: + try: + subkey = win32api.RegEnumKey(toolKey, 0) + except win32api.error: + break + win32api.RegDeleteKey(toolKey, subkey) + # Keys are now removed - write the new ones. + # But first check if we have the defaults - and if so, dont write anything! + if items == defaultToolMenuItems: + return + itemNo = 1 + for menu, cmd in items: + win32ui.WriteProfileVal("Tools Menu\\%s" % itemNo, "", menu) + win32ui.WriteProfileVal("Tools Menu\\%s" % itemNo, "Command", cmd) + itemNo = itemNo + 1 + + +def SetToolsMenu(menu, menuPos=None): + global tools + global idPos + + # todo - check the menu does not already exist. + # Create the new menu + toolsMenu = win32ui.CreatePopupMenu() + + # Load from the ini file. + items = LoadToolMenuItems() + for menuString, cmd in items: + tools[idPos] = (menuString, cmd, menuString) + toolsMenu.AppendMenu( + win32con.MF_ENABLED | win32con.MF_STRING, idPos, menuString + ) + win32ui.GetMainFrame().HookCommand(HandleToolCommand, idPos) + idPos = idPos + 1 + + # Find the correct spot to insert the new tools menu. + if menuPos is None: + menuPos = menu.GetMenuItemCount() - 2 + if menuPos < 0: + menuPos = 0 + + menu.InsertMenu( + menuPos, + win32con.MF_BYPOSITION + | win32con.MF_ENABLED + | win32con.MF_STRING + | win32con.MF_POPUP, + toolsMenu.GetHandle(), + "&Tools", + ) + + +def HandleToolCommand(cmd, code): + import re + import traceback + + global tools + (menuString, pyCmd, desc) = tools[cmd] + win32ui.SetStatusText("Executing tool %s" % desc, 1) + pyCmd = re.sub("\\\\n", "\n", pyCmd) + win32ui.DoWaitCursor(1) + oldFlag = None + try: + oldFlag = sys.stdout.template.writeQueueing + sys.stdout.template.writeQueueing = 0 + except (NameError, AttributeError): + pass + + try: + exec("%s\n" % pyCmd) + worked = 1 + except SystemExit: + # The program raised a SystemExit - ignore it. + worked = 1 + except: + print("Failed to execute command:\n%s" % pyCmd) + traceback.print_exc() + worked = 0 + if oldFlag is not None: + sys.stdout.template.writeQueueing = oldFlag + win32ui.DoWaitCursor(0) + if worked: + text = "Completed successfully." + else: + text = "Error executing %s." % desc + win32ui.SetStatusText(text, 1) + + +# The property page for maintaing the items on the Tools menu. +import commctrl +from pywin.mfc import dialog + +if win32ui.UNICODE: + LVN_ENDLABELEDIT = commctrl.LVN_ENDLABELEDITW +else: + LVN_ENDLABELEDIT = commctrl.LVN_ENDLABELEDITA + + +class ToolMenuPropPage(dialog.PropertyPage): + def __init__(self): + self.bImChangingEditControls = 0 # Am I programatically changing the controls? + dialog.PropertyPage.__init__(self, win32ui.IDD_PP_TOOLMENU) + + def OnInitDialog(self): + self.editMenuCommand = self.GetDlgItem(win32ui.IDC_EDIT2) + self.butNew = self.GetDlgItem(win32ui.IDC_BUTTON3) + + # Now hook the change notification messages for the edit controls. + self.HookCommand(self.OnCommandEditControls, win32ui.IDC_EDIT1) + self.HookCommand(self.OnCommandEditControls, win32ui.IDC_EDIT2) + + self.HookNotify(self.OnNotifyListControl, commctrl.LVN_ITEMCHANGED) + self.HookNotify(self.OnNotifyListControlEndLabelEdit, commctrl.LVN_ENDLABELEDIT) + + # Hook the button clicks. + self.HookCommand(self.OnButtonNew, win32ui.IDC_BUTTON3) # New Item + self.HookCommand(self.OnButtonDelete, win32ui.IDC_BUTTON4) # Delete item + self.HookCommand(self.OnButtonMove, win32ui.IDC_BUTTON1) # Move up + self.HookCommand(self.OnButtonMove, win32ui.IDC_BUTTON2) # Move down + + # Setup the columns in the list control + lc = self.GetDlgItem(win32ui.IDC_LIST1) + rect = lc.GetWindowRect() + cx = rect[2] - rect[0] + colSize = cx / 2 - win32api.GetSystemMetrics(win32con.SM_CXBORDER) - 1 + + item = commctrl.LVCFMT_LEFT, colSize, "Menu Text" + lc.InsertColumn(0, item) + + item = commctrl.LVCFMT_LEFT, colSize, "Python Command" + lc.InsertColumn(1, item) + + # Insert the existing tools menu + itemNo = 0 + for desc, cmd in LoadToolMenuItems(): + lc.InsertItem(itemNo, desc) + lc.SetItemText(itemNo, 1, cmd) + itemNo = itemNo + 1 + + self.listControl = lc + return dialog.PropertyPage.OnInitDialog(self) + + def OnOK(self): + # Write the menu back to the registry. + items = [] + itemLook = 0 + while 1: + try: + text = self.listControl.GetItemText(itemLook, 0) + if not text: + break + items.append((text, self.listControl.GetItemText(itemLook, 1))) + except win32ui.error: + # no more items! + break + itemLook = itemLook + 1 + WriteToolMenuItems(items) + return self._obj_.OnOK() + + def OnCommandEditControls(self, id, cmd): + # print "OnEditControls", id, cmd + if cmd == win32con.EN_CHANGE and not self.bImChangingEditControls: + itemNo = self.listControl.GetNextItem(-1, commctrl.LVNI_SELECTED) + newText = self.editMenuCommand.GetWindowText() + self.listControl.SetItemText(itemNo, 1, newText) + + return 0 + + def OnNotifyListControlEndLabelEdit(self, id, cmd): + newText = self.listControl.GetEditControl().GetWindowText() + itemNo = self.listControl.GetNextItem(-1, commctrl.LVNI_SELECTED) + self.listControl.SetItemText(itemNo, 0, newText) + + def OnNotifyListControl(self, id, cmd): + # print id, cmd + try: + itemNo = self.listControl.GetNextItem(-1, commctrl.LVNI_SELECTED) + except win32ui.error: # No selection! + return + + self.bImChangingEditControls = 1 + try: + item = self.listControl.GetItem(itemNo, 1) + self.editMenuCommand.SetWindowText(item[4]) + finally: + self.bImChangingEditControls = 0 + + return 0 # we have handled this! + + def OnButtonNew(self, id, cmd): + if cmd == win32con.BN_CLICKED: + newIndex = self.listControl.GetItemCount() + self.listControl.InsertItem(newIndex, "Click to edit the text") + self.listControl.EnsureVisible(newIndex, 0) + + def OnButtonMove(self, id, cmd): + if cmd == win32con.BN_CLICKED: + try: + itemNo = self.listControl.GetNextItem(-1, commctrl.LVNI_SELECTED) + except win32ui.error: + return + menu = self.listControl.GetItemText(itemNo, 0) + cmd = self.listControl.GetItemText(itemNo, 1) + if id == win32ui.IDC_BUTTON1: + # Move up + if itemNo > 0: + self.listControl.DeleteItem(itemNo) + # reinsert it. + self.listControl.InsertItem(itemNo - 1, menu) + self.listControl.SetItemText(itemNo - 1, 1, cmd) + else: + # Move down. + if itemNo < self.listControl.GetItemCount() - 1: + self.listControl.DeleteItem(itemNo) + # reinsert it. + self.listControl.InsertItem(itemNo + 1, menu) + self.listControl.SetItemText(itemNo + 1, 1, cmd) + + def OnButtonDelete(self, id, cmd): + if cmd == win32con.BN_CLICKED: + try: + itemNo = self.listControl.GetNextItem(-1, commctrl.LVNI_SELECTED) + except win32ui.error: # No selection! + return + self.listControl.DeleteItem(itemNo) diff --git a/myenv/Lib/site-packages/pythonwin/pywin/framework/window.py b/myenv/Lib/site-packages/pythonwin/pywin/framework/window.py new file mode 100644 index 000000000..58fd4d953 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/framework/window.py @@ -0,0 +1,14 @@ +# Framework Window classes. + +# Most Pythonwin windows should use these classes rather than +# the raw MFC ones if they want Pythonwin specific functionality. +import pywin.mfc.window +import win32con + + +class MDIChildWnd(pywin.mfc.window.MDIChildWnd): + def AutoRestore(self): + "If the window is minimised or maximised, restore it." + p = self.GetWindowPlacement() + if p[1] == win32con.SW_MINIMIZE or p[1] == win32con.SW_SHOWMINIMIZED: + self.SetWindowPlacement(p[0], win32con.SW_RESTORE, p[2], p[3], p[4]) diff --git a/myenv/Lib/site-packages/pythonwin/pywin/framework/winout.py b/myenv/Lib/site-packages/pythonwin/pywin/framework/winout.py new file mode 100644 index 000000000..e78e959a9 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/framework/winout.py @@ -0,0 +1,594 @@ +# winout.py +# +# generic "output window" +# +# This Window will detect itself closing, and recreate next time output is +# written to it. + +# This has the option of writing output at idle time (by hooking the +# idle message, and queueing output) or writing as each +# write is executed. +# Updating the window directly gives a jerky appearance as many writes +# take place between commands, and the windows scrolls, and updates etc +# Updating at idle-time may defer all output of a long process, giving the +# appearence nothing is happening. +# There is a compromise "line" mode, which will output whenever +# a complete line is available. + +# behaviour depends on self.writeQueueing + +# This module is thread safe - output can originate from any thread. If any thread +# other than the main thread attempts to print, it is always queued until next idle time + +import queue +import re + +import win32api +import win32con +import win32ui +from pywin.framework import app, window +from pywin.mfc import docview + +debug = lambda msg: None + +##debug=win32ui.OutputDebugString +##import win32trace;win32trace.InitWrite() # for debugging - delete me! +##debug = win32trace.write + + +class flags: + # queueing of output. + WQ_NONE = 0 + WQ_LINE = 1 + WQ_IDLE = 2 + + +# WindowOutputDocumentParent=docview.RichEditDoc +# WindowOutputDocumentParent=docview.Document +import pywin.scintilla.document +from pywin import default_scintilla_encoding +from pywin.scintilla import scintillacon + +WindowOutputDocumentParent = pywin.scintilla.document.CScintillaDocument + + +class WindowOutputDocument(WindowOutputDocumentParent): + def SaveModified(self): + return 1 # say it is OK to destroy my document + + def OnSaveDocument(self, fileName): + win32ui.SetStatusText("Saving file...", 1) + try: + self.SaveFile(fileName) + except IOError as details: + win32ui.MessageBox("Error - could not save file\r\n\r\n%s" % details) + return 0 + win32ui.SetStatusText("Ready") + return 1 + + +class WindowOutputFrame(window.MDIChildWnd): + def __init__(self, wnd=None): + window.MDIChildWnd.__init__(self, wnd) + self.HookMessage(self.OnSizeMove, win32con.WM_SIZE) + self.HookMessage(self.OnSizeMove, win32con.WM_MOVE) + + def LoadFrame(self, idResource, style, wndParent, context): + self.template = context.template + return self._obj_.LoadFrame(idResource, style, wndParent, context) + + def PreCreateWindow(self, cc): + cc = self._obj_.PreCreateWindow(cc) + if ( + self.template.defSize + and self.template.defSize[0] != self.template.defSize[1] + ): + rect = app.RectToCreateStructRect(self.template.defSize) + cc = cc[0], cc[1], cc[2], cc[3], rect, cc[5], cc[6], cc[7], cc[8] + return cc + + def OnSizeMove(self, msg): + # so recreate maintains position. + # Need to map coordinates from the + # frame windows first child. + mdiClient = self.GetParent() + self.template.defSize = mdiClient.ScreenToClient(self.GetWindowRect()) + + def OnDestroy(self, message): + self.template.OnFrameDestroy(self) + return 1 + + +class WindowOutputViewImpl: + def __init__(self): + self.patErrorMessage = re.compile('\W*File "(.*)", line ([0-9]+)') + self.template = self.GetDocument().GetDocTemplate() + + def HookHandlers(self): + # Hook for the right-click menu. + self.HookMessage(self.OnRClick, win32con.WM_RBUTTONDOWN) + + def OnDestroy(self, msg): + self.template.OnViewDestroy(self) + + def OnInitialUpdate(self): + self.RestoreKillBuffer() + self.SetSel(-2) # end of buffer + + def GetRightMenuItems(self): + ret = [] + flags = win32con.MF_STRING | win32con.MF_ENABLED + ret.append((flags, win32ui.ID_EDIT_COPY, "&Copy")) + ret.append((flags, win32ui.ID_EDIT_SELECT_ALL, "&Select all")) + return ret + + # + # Windows command handlers, virtuals, etc. + # + def OnRClick(self, params): + paramsList = self.GetRightMenuItems() + menu = win32ui.CreatePopupMenu() + for appendParams in paramsList: + if type(appendParams) != type(()): + appendParams = (appendParams,) + menu.AppendMenu(*appendParams) + menu.TrackPopupMenu(params[5]) # track at mouse position. + return 0 + + # as this is often used as an output window, exeptions will often + # be printed. Therefore, we support this functionality at this level. + # Returns TRUE if the current line is an error message line, and will + # jump to it. FALSE if no error (and no action taken) + def HandleSpecialLine(self): + from . import scriptutils + + line = self.GetLine() + if line[:11] == "com_error: ": + # An OLE Exception - pull apart the exception + # and try and locate a help file. + try: + import win32api + import win32con + + det = eval(line[line.find(":") + 1 :].strip()) + win32ui.SetStatusText("Opening help file on OLE error...") + from . import help + + help.OpenHelpFile(det[2][3], win32con.HELP_CONTEXT, det[2][4]) + return 1 + except win32api.error as details: + win32ui.SetStatusText( + "The help file could not be opened - %s" % details.strerror + ) + return 1 + except: + win32ui.SetStatusText( + "Line is a COM error, but no WinHelp details can be parsed" + ) + # Look for a Python traceback. + matchResult = self.patErrorMessage.match(line) + if matchResult is None: + # No match - try the previous line + lineNo = self.LineFromChar() + if lineNo > 0: + line = self.GetLine(lineNo - 1) + matchResult = self.patErrorMessage.match(line) + if matchResult is not None: + # we have an error line. + fileName = matchResult.group(1) + if fileName[0] == "<": + win32ui.SetStatusText("Can not load this file") + return 1 # still was an error message. + else: + lineNoString = matchResult.group(2) + # Attempt to locate the file (in case it is a relative spec) + fileNameSpec = fileName + fileName = scriptutils.LocatePythonFile(fileName) + if fileName is None: + # Dont force update, so it replaces the idle prompt. + win32ui.SetStatusText( + "Cant locate the file '%s'" % (fileNameSpec), 0 + ) + return 1 + + win32ui.SetStatusText( + "Jumping to line " + lineNoString + " of file " + fileName, 1 + ) + if not scriptutils.JumpToDocument(fileName, int(lineNoString)): + win32ui.SetStatusText("Could not open %s" % fileName) + return 1 # still was an error message. + return 1 + return 0 # not an error line + + def write(self, msg): + return self.template.write(msg) + + def writelines(self, lines): + for line in lines: + self.write(line) + + def flush(self): + self.template.flush() + + +class WindowOutputViewRTF(docview.RichEditView, WindowOutputViewImpl): + def __init__(self, doc): + docview.RichEditView.__init__(self, doc) + WindowOutputViewImpl.__init__(self) + + def OnInitialUpdate(self): + WindowOutputViewImpl.OnInitialUpdate(self) + return docview.RichEditView.OnInitialUpdate(self) + + def OnDestroy(self, msg): + WindowOutputViewImpl.OnDestroy(self, msg) + docview.RichEditView.OnDestroy(self, msg) + + def HookHandlers(self): + WindowOutputViewImpl.HookHandlers(self) + # Hook for finding and locating error messages + self.HookMessage(self.OnLDoubleClick, win32con.WM_LBUTTONDBLCLK) + + # docview.RichEditView.HookHandlers(self) + + def OnLDoubleClick(self, params): + if self.HandleSpecialLine(): + return 0 # dont pass on + return 1 # pass it on by default. + + def RestoreKillBuffer(self): + if len(self.template.killBuffer): + self.StreamIn(win32con.SF_RTF, self._StreamRTFIn) + self.template.killBuffer = [] + + def SaveKillBuffer(self): + self.StreamOut(win32con.SF_RTFNOOBJS, self._StreamRTFOut) + + def _StreamRTFOut(self, data): + self.template.killBuffer.append(data) + return 1 # keep em coming! + + def _StreamRTFIn(self, bytes): + try: + item = self.template.killBuffer[0] + self.template.killBuffer.remove(item) + if bytes < len(item): + print("Warning - output buffer not big enough!") + return item + except IndexError: + return None + + def dowrite(self, str): + self.SetSel(-2) + self.ReplaceSel(str) + + +import pywin.scintilla.view + + +class WindowOutputViewScintilla( + pywin.scintilla.view.CScintillaView, WindowOutputViewImpl +): + def __init__(self, doc): + pywin.scintilla.view.CScintillaView.__init__(self, doc) + WindowOutputViewImpl.__init__(self) + + def OnInitialUpdate(self): + pywin.scintilla.view.CScintillaView.OnInitialUpdate(self) + self.SCISetMarginWidth(3) + WindowOutputViewImpl.OnInitialUpdate(self) + + def OnDestroy(self, msg): + WindowOutputViewImpl.OnDestroy(self, msg) + pywin.scintilla.view.CScintillaView.OnDestroy(self, msg) + + def HookHandlers(self): + WindowOutputViewImpl.HookHandlers(self) + pywin.scintilla.view.CScintillaView.HookHandlers(self) + self.GetParent().HookNotify( + self.OnScintillaDoubleClick, scintillacon.SCN_DOUBLECLICK + ) + + ## self.HookMessage(self.OnLDoubleClick,win32con.WM_LBUTTONDBLCLK) + + def OnScintillaDoubleClick(self, std, extra): + self.HandleSpecialLine() + + ## def OnLDoubleClick(self,params): + ## return 0 # never dont pass on + + def RestoreKillBuffer(self): + assert len(self.template.killBuffer) in (0, 1), "Unexpected killbuffer contents" + if self.template.killBuffer: + self.SCIAddText(self.template.killBuffer[0]) + self.template.killBuffer = [] + + def SaveKillBuffer(self): + self.template.killBuffer = [self.GetTextRange(0, -1)] + + def dowrite(self, str): + end = self.GetTextLength() + atEnd = end == self.GetSel()[0] + self.SCIInsertText(str, end) + if atEnd: + self.SetSel(self.GetTextLength()) + + def SetWordWrap(self, bWrapOn=1): + if bWrapOn: + wrap_mode = scintillacon.SC_WRAP_WORD + else: + wrap_mode = scintillacon.SC_WRAP_NONE + self.SCISetWrapMode(wrap_mode) + + def _MakeColorizer(self): + return None # No colorizer for me! + + +WindowOutputView = WindowOutputViewScintilla + + +# The WindowOutput class is actually an MFC template. This is a conventient way of +# making sure that my state can exist beyond the life of the windows themselves. +# This is primarily to support the functionality of a WindowOutput window automatically +# being recreated if necessary when written to. +class WindowOutput(docview.DocTemplate): + """Looks like a general Output Window - text can be written by the 'write' method. + Will auto-create itself on first write, and also on next write after being closed""" + + softspace = 1 + + def __init__( + self, + title=None, + defSize=None, + queueing=flags.WQ_LINE, + bAutoRestore=1, + style=None, + makeDoc=None, + makeFrame=None, + makeView=None, + ): + """init the output window - + Params + title=None -- What is the title of the window + defSize=None -- What is the default size for the window - if this + is a string, the size will be loaded from the ini file. + queueing = flags.WQ_LINE -- When should output be written + bAutoRestore=1 -- Should a minimized window be restored. + style -- Style for Window, or None for default. + makeDoc, makeFrame, makeView -- Classes for frame, view and window respectively. + """ + if makeDoc is None: + makeDoc = WindowOutputDocument + if makeFrame is None: + makeFrame = WindowOutputFrame + if makeView is None: + makeView = WindowOutputViewScintilla + docview.DocTemplate.__init__( + self, win32ui.IDR_PYTHONTYPE, makeDoc, makeFrame, makeView + ) + self.SetDocStrings("\nOutput\n\nText Documents (*.txt)\n.txt\n\n\n") + win32ui.GetApp().AddDocTemplate(self) + self.writeQueueing = queueing + self.errorCantRecreate = 0 + self.killBuffer = [] + self.style = style + self.bAutoRestore = bAutoRestore + self.title = title + self.bCreating = 0 + self.interruptCount = 0 + if type(defSize) == type(""): # is a string - maintain size pos from ini file. + self.iniSizeSection = defSize + self.defSize = app.LoadWindowSize(defSize) + self.loadedSize = self.defSize + else: + self.iniSizeSection = None + self.defSize = defSize + self.currentView = None + self.outputQueue = queue.Queue(-1) + self.mainThreadId = win32api.GetCurrentThreadId() + self.idleHandlerSet = 0 + self.SetIdleHandler() + + def __del__(self): + self.Close() + + def Create(self, title=None, style=None): + self.bCreating = 1 + if title: + self.title = title + if style: + self.style = style + doc = self.OpenDocumentFile() + if doc is None: + return + self.currentView = doc.GetFirstView() + self.bCreating = 0 + if self.title: + doc.SetTitle(self.title) + + def Close(self): + self.RemoveIdleHandler() + try: + parent = self.currentView.GetParent() + except (AttributeError, win32ui.error): # Already closed + return + parent.DestroyWindow() + + def SetTitle(self, title): + self.title = title + if self.currentView: + self.currentView.GetDocument().SetTitle(self.title) + + def OnViewDestroy(self, view): + self.currentView.SaveKillBuffer() + self.currentView = None + + def OnFrameDestroy(self, frame): + if self.iniSizeSection: + # use GetWindowPlacement(), as it works even when min'd or max'd + newSize = frame.GetWindowPlacement()[4] + if self.loadedSize != newSize: + app.SaveWindowSize(self.iniSizeSection, newSize) + + def SetIdleHandler(self): + if not self.idleHandlerSet: + debug("Idle handler set\n") + win32ui.GetApp().AddIdleHandler(self.QueueIdleHandler) + self.idleHandlerSet = 1 + + def RemoveIdleHandler(self): + if self.idleHandlerSet: + debug("Idle handler reset\n") + if win32ui.GetApp().DeleteIdleHandler(self.QueueIdleHandler) == 0: + debug("Error deleting idle handler\n") + self.idleHandlerSet = 0 + + def RecreateWindow(self): + if self.errorCantRecreate: + debug("Error = not trying again") + return 0 + try: + # This will fail if app shutting down + win32ui.GetMainFrame().GetSafeHwnd() + self.Create() + return 1 + except (win32ui.error, AttributeError): + self.errorCantRecreate = 1 + debug("Winout can not recreate the Window!\n") + return 0 + + # this handles the idle message, and does the printing. + def QueueIdleHandler(self, handler, count): + try: + bEmpty = self.QueueFlush(20) + # If the queue is empty, then we are back to idle and restart interrupt logic. + if bEmpty: + self.interruptCount = 0 + except KeyboardInterrupt: + # First interrupt since idle we just pass on. + # later ones we dump the queue and give up. + self.interruptCount = self.interruptCount + 1 + if self.interruptCount > 1: + # Drop the queue quickly as the user is already annoyed :-) + self.outputQueue = queue.Queue(-1) + print("Interrupted.") + bEmpty = 1 + else: + raise # re-raise the error so the users exception filters up. + return not bEmpty # More to do if not empty. + + # Returns true if the Window needs to be recreated. + def NeedRecreateWindow(self): + try: + if self.currentView is not None and self.currentView.IsWindow(): + return 0 + except ( + win32ui.error, + AttributeError, + ): # Attribute error if the win32ui object has died. + pass + return 1 + + # Returns true if the Window is OK (either cos it was, or because it was recreated + def CheckRecreateWindow(self): + if self.bCreating: + return 1 + if not self.NeedRecreateWindow(): + return 1 + if self.bAutoRestore: + if self.RecreateWindow(): + return 1 + return 0 + + def QueueFlush(self, max=None): + # Returns true if the queue is empty after the flush + # debug("Queueflush - %d, %d\n" % (max, self.outputQueue.qsize())) + if self.bCreating: + return 1 + items = [] + rc = 0 + while max is None or max > 0: + try: + item = self.outputQueue.get_nowait() + items.append(item) + except queue.Empty: + rc = 1 + break + if max is not None: + max = max - 1 + if len(items) != 0: + if not self.CheckRecreateWindow(): + debug(":Recreate failed!\n") + return 1 # In trouble - so say we have nothing to do. + win32ui.PumpWaitingMessages() # Pump paint messages + self.currentView.dowrite("".join(items)) + return rc + + def HandleOutput(self, message): + # debug("QueueOutput on thread %d, flags %d with '%s'...\n" % (win32api.GetCurrentThreadId(), self.writeQueueing, message )) + self.outputQueue.put(message) + if win32api.GetCurrentThreadId() != self.mainThreadId: + pass + # debug("not my thread - ignoring queue options!\n") + elif self.writeQueueing == flags.WQ_LINE: + pos = message.rfind("\n") + if pos >= 0: + # debug("Line queueing - forcing flush\n") + self.QueueFlush() + return + elif self.writeQueueing == flags.WQ_NONE: + # debug("WQ_NONE - flushing!\n") + self.QueueFlush() + return + # Let our idle handler get it - wake it up + try: + win32ui.GetMainFrame().PostMessage( + win32con.WM_USER + ) # Kick main thread off. + except win32ui.error: + # This can happen as the app is shutting down, so we send it to the C++ debugger + win32api.OutputDebugString(message) + + # delegate certain fns to my view. + def writelines(self, lines): + for line in lines: + self.write(line) + + def write(self, message): + self.HandleOutput(message) + + def flush(self): + self.QueueFlush() + + def HandleSpecialLine(self): + self.currentView.HandleSpecialLine() + + +def RTFWindowOutput(*args, **kw): + kw["makeView"] = WindowOutputViewRTF + return WindowOutput(*args, **kw) + + +def thread_test(o): + for i in range(5): + o.write("Hi from thread %d\n" % (win32api.GetCurrentThreadId())) + win32api.Sleep(100) + + +def test(): + w = WindowOutput(queueing=flags.WQ_IDLE) + w.write("First bit of text\n") + import _thread + + for i in range(5): + w.write("Hello from the main thread\n") + _thread.start_new(thread_test, (w,)) + for i in range(2): + w.write("Hello from the main thread\n") + win32api.Sleep(50) + return w + + +if __name__ == "__main__": + test() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/idle/AutoExpand.py b/myenv/Lib/site-packages/pythonwin/pywin/idle/AutoExpand.py new file mode 100644 index 000000000..3b302ee93 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/idle/AutoExpand.py @@ -0,0 +1,95 @@ +import re +import string + +###$ event <> +###$ win +###$ unix + + +class AutoExpand: + keydefs = { + "<>": [""], + } + + unix_keydefs = { + "<>": [""], + } + + menudefs = [ + ( + "edit", + [ + ("E_xpand word", "<>"), + ], + ), + ] + + wordchars = string.ascii_letters + string.digits + "_" + + def __init__(self, editwin): + self.text = editwin.text + self.text.wordlist = None # XXX what is this? + self.state = None + + def expand_word_event(self, event): + curinsert = self.text.index("insert") + curline = self.text.get("insert linestart", "insert lineend") + if not self.state: + words = self.getwords() + index = 0 + else: + words, index, insert, line = self.state + if insert != curinsert or line != curline: + words = self.getwords() + index = 0 + if not words: + self.text.bell() + return "break" + word = self.getprevword() + self.text.delete("insert - %d chars" % len(word), "insert") + newword = words[index] + index = (index + 1) % len(words) + if index == 0: + self.text.bell() # Warn we cycled around + self.text.insert("insert", newword) + curinsert = self.text.index("insert") + curline = self.text.get("insert linestart", "insert lineend") + self.state = words, index, curinsert, curline + return "break" + + def getwords(self): + word = self.getprevword() + if not word: + return [] + before = self.text.get("1.0", "insert wordstart") + wbefore = re.findall(r"\b" + word + r"\w+\b", before) + del before + after = self.text.get("insert wordend", "end") + wafter = re.findall(r"\b" + word + r"\w+\b", after) + del after + if not wbefore and not wafter: + return [] + words = [] + dict = {} + # search backwards through words before + wbefore.reverse() + for w in wbefore: + if dict.get(w): + continue + words.append(w) + dict[w] = w + # search onwards through words after + for w in wafter: + if dict.get(w): + continue + words.append(w) + dict[w] = w + words.append(word) + return words + + def getprevword(self): + line = self.text.get("insert linestart", "insert") + i = len(line) + while i > 0 and line[i - 1] in self.wordchars: + i = i - 1 + return line[i:] diff --git a/myenv/Lib/site-packages/pythonwin/pywin/idle/AutoIndent.py b/myenv/Lib/site-packages/pythonwin/pywin/idle/AutoIndent.py new file mode 100644 index 000000000..c5adc5f86 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/idle/AutoIndent.py @@ -0,0 +1,547 @@ +import sys +import tokenize + +from pywin import default_scintilla_encoding + +from . import PyParse + +if sys.version_info < (3,): + # in py2k, tokenize() takes a 'token eater' callback, while + # generate_tokens is a generator that works with str objects. + token_generator = tokenize.generate_tokens +else: + # in py3k tokenize() is the generator working with 'byte' objects, and + # token_generator is the 'undocumented b/w compat' function that + # theoretically works with str objects - but actually seems to fail) + token_generator = tokenize.tokenize + + +class AutoIndent: + menudefs = [ + ( + "edit", + [ + None, + ("_Indent region", "<>"), + ("_Dedent region", "<>"), + ("Comment _out region", "<>"), + ("U_ncomment region", "<>"), + ("Tabify region", "<>"), + ("Untabify region", "<>"), + ("Toggle tabs", "<>"), + ("New indent width", "<>"), + ], + ), + ] + + keydefs = { + "<>": [""], + "<>": ["", ""], + "<>": [""], + } + + windows_keydefs = { + "<>": [""], + "<>": [""], + "<>": [""], + "<>": [""], + "<>": [""], + "<>": [""], + "<>": [""], + "<>": [""], + } + + unix_keydefs = { + "<>": [ + "", + "", + "", + ], + "<>": [ + "", + "", + "", + ], + "<>": ["", ""], + "<>": ["", ""], + "<>": ["", ""], + "<>": ["", ""], + "<>": [""], + "<>": [""], + } + + # usetabs true -> literal tab characters are used by indent and + # dedent cmds, possibly mixed with spaces if + # indentwidth is not a multiple of tabwidth + # false -> tab characters are converted to spaces by indent + # and dedent cmds, and ditto TAB keystrokes + # indentwidth is the number of characters per logical indent level. + # tabwidth is the display width of a literal tab character. + # CAUTION: telling Tk to use anything other than its default + # tab setting causes it to use an entirely different tabbing algorithm, + # treating tab stops as fixed distances from the left margin. + # Nobody expects this, so for now tabwidth should never be changed. + usetabs = 1 + indentwidth = 4 + tabwidth = 8 # for IDLE use, must remain 8 until Tk is fixed + + # If context_use_ps1 is true, parsing searches back for a ps1 line; + # else searches for a popular (if, def, ...) Python stmt. + context_use_ps1 = 0 + + # When searching backwards for a reliable place to begin parsing, + # first start num_context_lines[0] lines back, then + # num_context_lines[1] lines back if that didn't work, and so on. + # The last value should be huge (larger than the # of lines in a + # conceivable file). + # Making the initial values larger slows things down more often. + num_context_lines = 50, 500, 5000000 + + def __init__(self, editwin): + self.editwin = editwin + self.text = editwin.text + + def config(self, **options): + for key, value in options.items(): + if key == "usetabs": + self.usetabs = value + elif key == "indentwidth": + self.indentwidth = value + elif key == "tabwidth": + self.tabwidth = value + elif key == "context_use_ps1": + self.context_use_ps1 = value + else: + raise KeyError("bad option name: %s" % repr(key)) + + # If ispythonsource and guess are true, guess a good value for + # indentwidth based on file content (if possible), and if + # indentwidth != tabwidth set usetabs false. + # In any case, adjust the Text widget's view of what a tab + # character means. + + def set_indentation_params(self, ispythonsource, guess=1): + if guess and ispythonsource: + i = self.guess_indent() + if 2 <= i <= 8: + self.indentwidth = i + if self.indentwidth != self.tabwidth: + self.usetabs = 0 + + self.editwin.set_tabwidth(self.tabwidth) + + def smart_backspace_event(self, event): + text = self.text + first, last = self.editwin.get_selection_indices() + if first and last: + text.delete(first, last) + text.mark_set("insert", first) + return "break" + # Delete whitespace left, until hitting a real char or closest + # preceding virtual tab stop. + chars = text.get("insert linestart", "insert") + if chars == "": + if text.compare("insert", ">", "1.0"): + # easy: delete preceding newline + text.delete("insert-1c") + else: + text.bell() # at start of buffer + return "break" + if chars[-1] not in " \t": + # easy: delete preceding real char + text.delete("insert-1c") + return "break" + # Ick. It may require *inserting* spaces if we back up over a + # tab character! This is written to be clear, not fast. + have = len(chars.expandtabs(self.tabwidth)) + assert have > 0 + want = int((have - 1) / self.indentwidth) * self.indentwidth + ncharsdeleted = 0 + while 1: + chars = chars[:-1] + ncharsdeleted = ncharsdeleted + 1 + have = len(chars.expandtabs(self.tabwidth)) + if have <= want or chars[-1] not in " \t": + break + text.undo_block_start() + text.delete("insert-%dc" % ncharsdeleted, "insert") + if have < want: + text.insert("insert", " " * (want - have)) + text.undo_block_stop() + return "break" + + def smart_indent_event(self, event): + # if intraline selection: + # delete it + # elif multiline selection: + # do indent-region & return + # indent one level + text = self.text + first, last = self.editwin.get_selection_indices() + text.undo_block_start() + try: + if first and last: + if index2line(first) != index2line(last): + return self.indent_region_event(event) + text.delete(first, last) + text.mark_set("insert", first) + prefix = text.get("insert linestart", "insert") + raw, effective = classifyws(prefix, self.tabwidth) + if raw == len(prefix): + # only whitespace to the left + self.reindent_to(effective + self.indentwidth) + else: + if self.usetabs: + pad = "\t" + else: + effective = len(prefix.expandtabs(self.tabwidth)) + n = self.indentwidth + pad = " " * (n - effective % n) + text.insert("insert", pad) + text.see("insert") + return "break" + finally: + text.undo_block_stop() + + def newline_and_indent_event(self, event): + text = self.text + first, last = self.editwin.get_selection_indices() + text.undo_block_start() + try: + if first and last: + text.delete(first, last) + text.mark_set("insert", first) + line = text.get("insert linestart", "insert") + i, n = 0, len(line) + while i < n and line[i] in " \t": + i = i + 1 + if i == n: + # the cursor is in or at leading indentation; just inject + # an empty line at the start and strip space from current line + text.delete("insert - %d chars" % i, "insert") + text.insert("insert linestart", "\n") + return "break" + indent = line[:i] + # strip whitespace before insert point + i = 0 + while line and line[-1] in " \t": + line = line[:-1] + i = i + 1 + if i: + text.delete("insert - %d chars" % i, "insert") + # strip whitespace after insert point + while text.get("insert") in " \t": + text.delete("insert") + # start new line + text.insert("insert", "\n") + + # adjust indentation for continuations and block + # open/close first need to find the last stmt + lno = index2line(text.index("insert")) + y = PyParse.Parser(self.indentwidth, self.tabwidth) + for context in self.num_context_lines: + startat = max(lno - context, 1) + startatindex = repr(startat) + ".0" + rawtext = text.get(startatindex, "insert") + y.set_str(rawtext) + bod = y.find_good_parse_start( + self.context_use_ps1, self._build_char_in_string_func(startatindex) + ) + if bod is not None or startat == 1: + break + y.set_lo(bod or 0) + c = y.get_continuation_type() + if c != PyParse.C_NONE: + # The current stmt hasn't ended yet. + if c == PyParse.C_STRING: + # inside a string; just mimic the current indent + text.insert("insert", indent) + elif c == PyParse.C_BRACKET: + # line up with the first (if any) element of the + # last open bracket structure; else indent one + # level beyond the indent of the line with the + # last open bracket + self.reindent_to(y.compute_bracket_indent()) + elif c == PyParse.C_BACKSLASH: + # if more than one line in this stmt already, just + # mimic the current indent; else if initial line + # has a start on an assignment stmt, indent to + # beyond leftmost =; else to beyond first chunk of + # non-whitespace on initial line + if y.get_num_lines_in_stmt() > 1: + text.insert("insert", indent) + else: + self.reindent_to(y.compute_backslash_indent()) + else: + assert 0, "bogus continuation type " + repr(c) + return "break" + + # This line starts a brand new stmt; indent relative to + # indentation of initial line of closest preceding + # interesting stmt. + indent = y.get_base_indent_string() + text.insert("insert", indent) + if y.is_block_opener(): + self.smart_indent_event(event) + elif indent and y.is_block_closer(): + self.smart_backspace_event(event) + return "break" + finally: + text.see("insert") + text.undo_block_stop() + + auto_indent = newline_and_indent_event + + # Our editwin provides a is_char_in_string function that works + # with a Tk text index, but PyParse only knows about offsets into + # a string. This builds a function for PyParse that accepts an + # offset. + + def _build_char_in_string_func(self, startindex): + def inner(offset, _startindex=startindex, _icis=self.editwin.is_char_in_string): + return _icis(_startindex + "+%dc" % offset) + + return inner + + def indent_region_event(self, event): + head, tail, chars, lines = self.get_region() + for pos in range(len(lines)): + line = lines[pos] + if line: + raw, effective = classifyws(line, self.tabwidth) + effective = effective + self.indentwidth + lines[pos] = self._make_blanks(effective) + line[raw:] + self.set_region(head, tail, chars, lines) + return "break" + + def dedent_region_event(self, event): + head, tail, chars, lines = self.get_region() + for pos in range(len(lines)): + line = lines[pos] + if line: + raw, effective = classifyws(line, self.tabwidth) + effective = max(effective - self.indentwidth, 0) + lines[pos] = self._make_blanks(effective) + line[raw:] + self.set_region(head, tail, chars, lines) + return "break" + + def comment_region_event(self, event): + head, tail, chars, lines = self.get_region() + for pos in range(len(lines) - 1): + line = lines[pos] + lines[pos] = "##" + line + self.set_region(head, tail, chars, lines) + + def uncomment_region_event(self, event): + head, tail, chars, lines = self.get_region() + for pos in range(len(lines)): + line = lines[pos] + if not line: + continue + if line[:2] == "##": + line = line[2:] + elif line[:1] == "#": + line = line[1:] + lines[pos] = line + self.set_region(head, tail, chars, lines) + + def tabify_region_event(self, event): + head, tail, chars, lines = self.get_region() + tabwidth = self._asktabwidth() + for pos in range(len(lines)): + line = lines[pos] + if line: + raw, effective = classifyws(line, tabwidth) + ntabs, nspaces = divmod(effective, tabwidth) + lines[pos] = "\t" * ntabs + " " * nspaces + line[raw:] + self.set_region(head, tail, chars, lines) + + def untabify_region_event(self, event): + head, tail, chars, lines = self.get_region() + tabwidth = self._asktabwidth() + for pos in range(len(lines)): + lines[pos] = lines[pos].expandtabs(tabwidth) + self.set_region(head, tail, chars, lines) + + def toggle_tabs_event(self, event): + if self.editwin.askyesno( + "Toggle tabs", + "Turn tabs " + ("on", "off")[self.usetabs] + "?", + parent=self.text, + ): + self.usetabs = not self.usetabs + return "break" + + # XXX this isn't bound to anything -- see class tabwidth comments + def change_tabwidth_event(self, event): + new = self._asktabwidth() + if new != self.tabwidth: + self.tabwidth = new + self.set_indentation_params(0, guess=0) + return "break" + + def change_indentwidth_event(self, event): + new = self.editwin.askinteger( + "Indent width", + "New indent width (1-16)", + parent=self.text, + initialvalue=self.indentwidth, + minvalue=1, + maxvalue=16, + ) + if new and new != self.indentwidth: + self.indentwidth = new + return "break" + + def get_region(self): + text = self.text + first, last = self.editwin.get_selection_indices() + if first and last: + head = text.index(first + " linestart") + tail = text.index(last + "-1c lineend +1c") + else: + head = text.index("insert linestart") + tail = text.index("insert lineend +1c") + chars = text.get(head, tail) + lines = chars.split("\n") + return head, tail, chars, lines + + def set_region(self, head, tail, chars, lines): + text = self.text + newchars = "\n".join(lines) + if newchars == chars: + text.bell() + return + text.tag_remove("sel", "1.0", "end") + text.mark_set("insert", head) + text.undo_block_start() + text.delete(head, tail) + text.insert(head, newchars) + text.undo_block_stop() + text.tag_add("sel", head, "insert") + + # Make string that displays as n leading blanks. + + def _make_blanks(self, n): + if self.usetabs: + ntabs, nspaces = divmod(n, self.tabwidth) + return "\t" * ntabs + " " * nspaces + else: + return " " * n + + # Delete from beginning of line to insert point, then reinsert + # column logical (meaning use tabs if appropriate) spaces. + + def reindent_to(self, column): + text = self.text + text.undo_block_start() + if text.compare("insert linestart", "!=", "insert"): + text.delete("insert linestart", "insert") + if column: + text.insert("insert", self._make_blanks(column)) + text.undo_block_stop() + + def _asktabwidth(self): + return ( + self.editwin.askinteger( + "Tab width", + "Spaces per tab?", + parent=self.text, + initialvalue=self.tabwidth, + minvalue=1, + maxvalue=16, + ) + or self.tabwidth + ) + + # Guess indentwidth from text content. + # Return guessed indentwidth. This should not be believed unless + # it's in a reasonable range (e.g., it will be 0 if no indented + # blocks are found). + + def guess_indent(self): + opener, indented = IndentSearcher(self.text, self.tabwidth).run() + if opener and indented: + raw, indentsmall = classifyws(opener, self.tabwidth) + raw, indentlarge = classifyws(indented, self.tabwidth) + else: + indentsmall = indentlarge = 0 + return indentlarge - indentsmall + + +# "line.col" -> line, as an int +def index2line(index): + return int(float(index)) + + +# Look at the leading whitespace in s. +# Return pair (# of leading ws characters, +# effective # of leading blanks after expanding +# tabs to width tabwidth) + + +def classifyws(s, tabwidth): + raw = effective = 0 + for ch in s: + if ch == " ": + raw = raw + 1 + effective = effective + 1 + elif ch == "\t": + raw = raw + 1 + effective = (effective // tabwidth + 1) * tabwidth + else: + break + return raw, effective + + +class IndentSearcher: + # .run() chews over the Text widget, looking for a block opener + # and the stmt following it. Returns a pair, + # (line containing block opener, line containing stmt) + # Either or both may be None. + + def __init__(self, text, tabwidth): + self.text = text + self.tabwidth = tabwidth + self.i = self.finished = 0 + self.blkopenline = self.indentedline = None + + def readline(self): + if self.finished: + val = "" + else: + i = self.i = self.i + 1 + mark = repr(i) + ".0" + if self.text.compare(mark, ">=", "end"): + val = "" + else: + val = self.text.get(mark, mark + " lineend+1c") + # hrm - not sure this is correct in py3k - the source code may have + # an encoding declared, but the data will *always* be in + # default_scintilla_encoding - so if anyone looks at the encoding decl + # in the source they will be wrong. I think. Maybe. Or something... + return val.encode(default_scintilla_encoding) + + def run(self): + OPENERS = ("class", "def", "for", "if", "try", "while") + INDENT = tokenize.INDENT + NAME = tokenize.NAME + + save_tabsize = tokenize.tabsize + tokenize.tabsize = self.tabwidth + try: + try: + for typ, token, start, end, line in token_generator(self.readline): + if typ == NAME and token in OPENERS: + self.blkopenline = line + elif typ == INDENT and self.blkopenline: + self.indentedline = line + break + + except (tokenize.TokenError, IndentationError): + # since we cut off the tokenizer early, we can trigger + # spurious errors + pass + finally: + tokenize.tabsize = save_tabsize + return self.blkopenline, self.indentedline diff --git a/myenv/Lib/site-packages/pythonwin/pywin/idle/CallTips.py b/myenv/Lib/site-packages/pythonwin/pywin/idle/CallTips.py new file mode 100644 index 000000000..cecc760a1 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/idle/CallTips.py @@ -0,0 +1,219 @@ +# CallTips.py - An IDLE extension that provides "Call Tips" - ie, a floating window that +# displays parameter information as you open parens. + +import inspect +import string +import sys +import traceback + + +class CallTips: + menudefs = [] + + keydefs = { + "<>": [""], + "<>": [""], + "<>": [""], + "<>": ["", ""], + } + + windows_keydefs = {} + + unix_keydefs = {} + + def __init__(self, editwin): + self.editwin = editwin + self.text = editwin.text + self.calltip = None + if hasattr(self.text, "make_calltip_window"): + self._make_calltip_window = self.text.make_calltip_window + else: + self._make_calltip_window = self._make_tk_calltip_window + + def close(self): + self._make_calltip_window = None + + # Makes a Tk based calltip window. Used by IDLE, but not Pythonwin. + # See __init__ above for how this is used. + def _make_tk_calltip_window(self): + import CallTipWindow + + return CallTipWindow.CallTip(self.text) + + def _remove_calltip_window(self): + if self.calltip: + self.calltip.hidetip() + self.calltip = None + + def paren_open_event(self, event): + self._remove_calltip_window() + arg_text = get_arg_text(self.get_object_at_cursor()) + if arg_text: + self.calltip_start = self.text.index("insert") + self.calltip = self._make_calltip_window() + self.calltip.showtip(arg_text) + return "" # so the event is handled normally. + + def paren_close_event(self, event): + # Now just hides, but later we should check if other + # paren'd expressions remain open. + self._remove_calltip_window() + return "" # so the event is handled normally. + + def check_calltip_cancel_event(self, event): + if self.calltip: + # If we have moved before the start of the calltip, + # or off the calltip line, then cancel the tip. + # (Later need to be smarter about multi-line, etc) + if self.text.compare( + "insert", "<=", self.calltip_start + ) or self.text.compare("insert", ">", self.calltip_start + " lineend"): + self._remove_calltip_window() + return "" # so the event is handled normally. + + def calltip_cancel_event(self, event): + self._remove_calltip_window() + return "" # so the event is handled normally. + + def get_object_at_cursor( + self, + wordchars="._" + + string.ascii_uppercase + + string.ascii_lowercase + + string.digits, + ): + # XXX - This needs to be moved to a better place + # so the "." attribute lookup code can also use it. + text = self.text + chars = text.get("insert linestart", "insert") + i = len(chars) + while i and chars[i - 1] in wordchars: + i = i - 1 + word = chars[i:] + if word: + # How is this for a hack! + import __main__ + + namespace = sys.modules.copy() + namespace.update(__main__.__dict__) + try: + return eval(word, namespace) + except: + pass + return None # Can't find an object. + + +def _find_constructor(class_ob): + # Given a class object, return a function object used for the + # constructor (ie, __init__() ) or None if we can't find one. + try: + return class_ob.__init__ + except AttributeError: + for base in class_ob.__bases__: + rc = _find_constructor(base) + if rc is not None: + return rc + return None + + +def get_arg_text(ob): + # Get a string describing the arguments for the given object. + argText = "" + if ob is not None: + if inspect.isclass(ob): + # Look for the highest __init__ in the class chain. + fob = _find_constructor(ob) + if fob is None: + fob = lambda: None + else: + fob = ob + if inspect.isfunction(fob) or inspect.ismethod(fob): + try: + argText = str(inspect.signature(fob)) + except: + print("Failed to format the args") + traceback.print_exc() + # See if we can use the docstring + if hasattr(ob, "__doc__"): + doc = ob.__doc__ + try: + doc = doc.strip() + pos = doc.find("\n") + except AttributeError: + ## New style classes may have __doc__ slot without actually + ## having a string assigned to it + pass + else: + if pos < 0 or pos > 70: + pos = 70 + if argText: + argText = argText + "\n" + argText = argText + doc[:pos] + + return argText + + +################################################# +# +# Test code +# +if __name__ == "__main__": + + def t1(): + "()" + + def t2(a, b=None): + "(a, b=None)" + + def t3(a, *args): + "(a, *args)" + + def t4(*args): + "(*args)" + + def t5(a, *args): + "(a, *args)" + + def t6(a, b=None, *args, **kw): + "(a, b=None, *args, **kw)" + + class TC: + "(self, a=None, *b)" + + def __init__(self, a=None, *b): + "(self, a=None, *b)" + + def t1(self): + "(self)" + + def t2(self, a, b=None): + "(self, a, b=None)" + + def t3(self, a, *args): + "(self, a, *args)" + + def t4(self, *args): + "(self, *args)" + + def t5(self, a, *args): + "(self, a, *args)" + + def t6(self, a, b=None, *args, **kw): + "(self, a, b=None, *args, **kw)" + + def test(tests): + failed = [] + for t in tests: + expected = t.__doc__ + "\n" + t.__doc__ + if get_arg_text(t) != expected: + failed.append(t) + print( + "%s - expected %s, but got %s" + % (t, repr(expected), repr(get_arg_text(t))) + ) + print("%d of %d tests failed" % (len(failed), len(tests))) + + tc = TC() + tests = t1, t2, t3, t4, t5, t6, TC, tc.t1, tc.t2, tc.t3, tc.t4, tc.t5, tc.t6 + + test(tests) diff --git a/myenv/Lib/site-packages/pythonwin/pywin/idle/FormatParagraph.py b/myenv/Lib/site-packages/pythonwin/pywin/idle/FormatParagraph.py new file mode 100644 index 000000000..143c18ee3 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/idle/FormatParagraph.py @@ -0,0 +1,166 @@ +# Extension to format a paragraph + +# Does basic, standard text formatting, and also understands Python +# comment blocks. Thus, for editing Python source code, this +# extension is really only suitable for reformatting these comment +# blocks or triple-quoted strings. + +# Known problems with comment reformatting: +# * If there is a selection marked, and the first line of the +# selection is not complete, the block will probably not be detected +# as comments, and will have the normal "text formatting" rules +# applied. +# * If a comment block has leading whitespace that mixes tabs and +# spaces, they will not be considered part of the same block. +# * Fancy comments, like this bulleted list, arent handled :-) + +import re + + +class FormatParagraph: + menudefs = [ + ( + "edit", + [ + ("Format Paragraph", "<>"), + ], + ) + ] + + keydefs = { + "<>": [""], + } + + unix_keydefs = { + "<>": [""], + } + + def __init__(self, editwin): + self.editwin = editwin + + def close(self): + self.editwin = None + + def format_paragraph_event(self, event): + text = self.editwin.text + first, last = self.editwin.get_selection_indices() + if first and last: + data = text.get(first, last) + comment_header = "" + else: + first, last, comment_header, data = find_paragraph( + text, text.index("insert") + ) + if comment_header: + # Reformat the comment lines - convert to text sans header. + lines = data.split("\n") + lines = map(lambda st, l=len(comment_header): st[l:], lines) + data = "\n".join(lines) + # Reformat to 70 chars or a 20 char width, whichever is greater. + format_width = max(70 - len(comment_header), 20) + newdata = reformat_paragraph(data, format_width) + # re-split and re-insert the comment header. + newdata = newdata.split("\n") + # If the block ends in a \n, we dont want the comment + # prefix inserted after it. (Im not sure it makes sense to + # reformat a comment block that isnt made of complete + # lines, but whatever!) Can't think of a clean soltution, + # so we hack away + block_suffix = "" + if not newdata[-1]: + block_suffix = "\n" + newdata = newdata[:-1] + builder = lambda item, prefix=comment_header: prefix + item + newdata = "\n".join([builder(d) for d in newdata]) + block_suffix + else: + # Just a normal text format + newdata = reformat_paragraph(data) + text.tag_remove("sel", "1.0", "end") + if newdata != data: + text.mark_set("insert", first) + text.undo_block_start() + text.delete(first, last) + text.insert(first, newdata) + text.undo_block_stop() + else: + text.mark_set("insert", last) + text.see("insert") + + +def find_paragraph(text, mark): + lineno, col = list(map(int, mark.split("."))) + line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno) + while text.compare("%d.0" % lineno, "<", "end") and is_all_white(line): + lineno = lineno + 1 + line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno) + first_lineno = lineno + comment_header = get_comment_header(line) + comment_header_len = len(comment_header) + while get_comment_header(line) == comment_header and not is_all_white( + line[comment_header_len:] + ): + lineno = lineno + 1 + line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno) + last = "%d.0" % lineno + # Search back to beginning of paragraph + lineno = first_lineno - 1 + line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno) + while ( + lineno > 0 + and get_comment_header(line) == comment_header + and not is_all_white(line[comment_header_len:]) + ): + lineno = lineno - 1 + line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno) + first = "%d.0" % (lineno + 1) + return first, last, comment_header, text.get(first, last) + + +def reformat_paragraph(data, limit=70): + lines = data.split("\n") + i = 0 + n = len(lines) + while i < n and is_all_white(lines[i]): + i = i + 1 + if i >= n: + return data + indent1 = get_indent(lines[i]) + if i + 1 < n and not is_all_white(lines[i + 1]): + indent2 = get_indent(lines[i + 1]) + else: + indent2 = indent1 + new = lines[:i] + partial = indent1 + while i < n and not is_all_white(lines[i]): + # XXX Should take double space after period (etc.) into account + words = re.split("(\s+)", lines[i]) + for j in range(0, len(words), 2): + word = words[j] + if not word: + continue # Can happen when line ends in whitespace + if len((partial + word).expandtabs()) > limit and partial != indent1: + new.append(partial.rstrip()) + partial = indent2 + partial = partial + word + " " + if j + 1 < len(words) and words[j + 1] != " ": + partial = partial + " " + i = i + 1 + new.append(partial.rstrip()) + # XXX Should reformat remaining paragraphs as well + new.extend(lines[i:]) + return "\n".join(new) + + +def is_all_white(line): + return re.match(r"^\s*$", line) is not None + + +def get_indent(line): + return re.match(r"^(\s*)", line).group() + + +def get_comment_header(line): + m = re.match(r"^(\s*#*)", line) + if m is None: + return "" + return m.group(1) diff --git a/myenv/Lib/site-packages/pythonwin/pywin/idle/IdleHistory.py b/myenv/Lib/site-packages/pythonwin/pywin/idle/IdleHistory.py new file mode 100644 index 000000000..24e692400 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/idle/IdleHistory.py @@ -0,0 +1,87 @@ +class History: + def __init__(self, text, output_sep="\n"): + self.text = text + self.history = [] + self.history_prefix = None + self.history_pointer = None + self.output_sep = output_sep + text.bind("<>", self.history_prev) + text.bind("<>", self.history_next) + + def history_next(self, event): + self.history_do(0) + return "break" + + def history_prev(self, event): + self.history_do(1) + return "break" + + def _get_source(self, start, end): + # Get source code from start index to end index. Lines in the + # text control may be separated by sys.ps2 . + lines = self.text.get(start, end).split(self.output_sep) + return "\n".join(lines) + + def _put_source(self, where, source): + output = self.output_sep.join(source.split("\n")) + self.text.insert(where, output) + + def history_do(self, reverse): + nhist = len(self.history) + pointer = self.history_pointer + prefix = self.history_prefix + if pointer is not None and prefix is not None: + if ( + self.text.compare("insert", "!=", "end-1c") + or self._get_source("iomark", "end-1c") != self.history[pointer] + ): + pointer = prefix = None + if pointer is None or prefix is None: + prefix = self._get_source("iomark", "end-1c") + if reverse: + pointer = nhist + else: + pointer = -1 + nprefix = len(prefix) + while 1: + if reverse: + pointer = pointer - 1 + else: + pointer = pointer + 1 + if pointer < 0 or pointer >= nhist: + self.text.bell() + if self._get_source("iomark", "end-1c") != prefix: + self.text.delete("iomark", "end-1c") + self._put_source("iomark", prefix) + pointer = prefix = None + break + item = self.history[pointer] + if item[:nprefix] == prefix and len(item) > nprefix: + self.text.delete("iomark", "end-1c") + self._put_source("iomark", item) + break + self.text.mark_set("insert", "end-1c") + self.text.see("insert") + self.text.tag_remove("sel", "1.0", "end") + self.history_pointer = pointer + self.history_prefix = prefix + + def history_store(self, source): + source = source.strip() + if len(source) > 2: + # avoid duplicates + try: + self.history.remove(source) + except ValueError: + pass + self.history.append(source) + self.history_pointer = None + self.history_prefix = None + + def recall(self, s): + s = s.strip() + self.text.tag_remove("sel", "1.0", "end") + self.text.delete("iomark", "end-1c") + self.text.mark_set("insert", "end-1c") + self.text.insert("insert", s) + self.text.see("insert") diff --git a/myenv/Lib/site-packages/pythonwin/pywin/idle/PyParse.py b/myenv/Lib/site-packages/pythonwin/pywin/idle/PyParse.py new file mode 100644 index 000000000..173336cad --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/idle/PyParse.py @@ -0,0 +1,591 @@ +import re +import string +import sys + +# Reason last stmt is continued (or C_NONE if it's not). +C_NONE, C_BACKSLASH, C_STRING, C_BRACKET = list(range(4)) + +if 0: # for throwaway debugging output + + def dump(*stuff): + sys.__stdout__.write(" ".join(map(str, stuff)) + "\n") + + +# Find what looks like the start of a popular stmt. + +_synchre = re.compile( + r""" + ^ + [ \t]* + (?: if + | for + | while + | else + | def + | return + | assert + | break + | class + | continue + | elif + | try + | except + | raise + | import + ) + \b +""", + re.VERBOSE | re.MULTILINE, +).search + +# Match blank line or non-indenting comment line. + +_junkre = re.compile( + r""" + [ \t]* + (?: \# \S .* )? + \n +""", + re.VERBOSE, +).match + +# Match any flavor of string; the terminating quote is optional +# so that we're robust in the face of incomplete program text. + +_match_stringre = re.compile( + r""" + \""" [^"\\]* (?: + (?: \\. | "(?!"") ) + [^"\\]* + )* + (?: \""" )? + +| " [^"\\\n]* (?: \\. [^"\\\n]* )* "? + +| ''' [^'\\]* (?: + (?: \\. | '(?!'') ) + [^'\\]* + )* + (?: ''' )? + +| ' [^'\\\n]* (?: \\. [^'\\\n]* )* '? +""", + re.VERBOSE | re.DOTALL, +).match + +# Match a line that starts with something interesting; +# used to find the first item of a bracket structure. + +_itemre = re.compile( + r""" + [ \t]* + [^\s#\\] # if we match, m.end()-1 is the interesting char +""", + re.VERBOSE, +).match + +# Match start of stmts that should be followed by a dedent. + +_closere = re.compile( + r""" + \s* + (?: return + | break + | continue + | raise + | pass + ) + \b +""", + re.VERBOSE, +).match + +# Chew up non-special chars as quickly as possible. If match is +# successful, m.end() less 1 is the index of the last boring char +# matched. If match is unsuccessful, the string starts with an +# interesting char. + +_chew_ordinaryre = re.compile( + r""" + [^[\](){}#'"\\]+ +""", + re.VERBOSE, +).match + +# Build translation table to map uninteresting chars to "x", open +# brackets to "(", and close brackets to ")". + +_tran = ["x"] * 256 +for ch in "({[": + _tran[ord(ch)] = "(" +for ch in ")}]": + _tran[ord(ch)] = ")" +for ch in "\"'\\\n#": + _tran[ord(ch)] = ch +# We are called with unicode strings, and str.translate is one of the few +# py2k functions which can't 'do the right thing' - so take care to ensure +# _tran is full of unicode... +_tran = "".join(_tran) +del ch + + +class Parser: + def __init__(self, indentwidth, tabwidth): + self.indentwidth = indentwidth + self.tabwidth = tabwidth + + def set_str(self, str): + assert len(str) == 0 or str[-1] == "\n", "Oops - have str %r" % (str,) + self.str = str + self.study_level = 0 + + # Return index of a good place to begin parsing, as close to the + # end of the string as possible. This will be the start of some + # popular stmt like "if" or "def". Return None if none found: + # the caller should pass more prior context then, if possible, or + # if not (the entire program text up until the point of interest + # has already been tried) pass 0 to set_lo. + # + # This will be reliable iff given a reliable is_char_in_string + # function, meaning that when it says "no", it's absolutely + # guaranteed that the char is not in a string. + # + # Ack, hack: in the shell window this kills us, because there's + # no way to tell the differences between output, >>> etc and + # user input. Indeed, IDLE's first output line makes the rest + # look like it's in an unclosed paren!: + # Python 1.5.2 (#0, Apr 13 1999, ... + + def find_good_parse_start(self, use_ps1, is_char_in_string=None): + str, pos = self.str, None + if use_ps1: + # shell window + ps1 = "\n" + sys.ps1 + i = str.rfind(ps1) + if i >= 0: + pos = i + len(ps1) + # make it look like there's a newline instead + # of ps1 at the start -- hacking here once avoids + # repeated hackery later + self.str = str[: pos - 1] + "\n" + str[pos:] + return pos + + # File window -- real work. + if not is_char_in_string: + # no clue -- make the caller pass everything + return None + + # Peek back from the end for a good place to start, + # but don't try too often; pos will be left None, or + # bumped to a legitimate synch point. + limit = len(str) + for tries in range(5): + i = str.rfind(":\n", 0, limit) + if i < 0: + break + i = str.rfind("\n", 0, i) + 1 # start of colon line + m = _synchre(str, i, limit) + if m and not is_char_in_string(m.start()): + pos = m.start() + break + limit = i + if pos is None: + # Nothing looks like a block-opener, or stuff does + # but is_char_in_string keeps returning true; most likely + # we're in or near a giant string, the colorizer hasn't + # caught up enough to be helpful, or there simply *aren't* + # any interesting stmts. In any of these cases we're + # going to have to parse the whole thing to be sure, so + # give it one last try from the start, but stop wasting + # time here regardless of the outcome. + m = _synchre(str) + if m and not is_char_in_string(m.start()): + pos = m.start() + return pos + + # Peeking back worked; look forward until _synchre no longer + # matches. + i = pos + 1 + while 1: + m = _synchre(str, i) + if m: + s, i = m.span() + if not is_char_in_string(s): + pos = s + else: + break + return pos + + # Throw away the start of the string. Intended to be called with + # find_good_parse_start's result. + + def set_lo(self, lo): + assert lo == 0 or self.str[lo - 1] == "\n" + if lo > 0: + self.str = self.str[lo:] + + # As quickly as humanly possible , find the line numbers (0- + # based) of the non-continuation lines. + # Creates self.{goodlines, continuation}. + + def _study1(self): + if self.study_level >= 1: + return + self.study_level = 1 + + # Map all uninteresting characters to "x", all open brackets + # to "(", all close brackets to ")", then collapse runs of + # uninteresting characters. This can cut the number of chars + # by a factor of 10-40, and so greatly speed the following loop. + str = self.str + str = str.translate(_tran) + str = str.replace("xxxxxxxx", "x") + str = str.replace("xxxx", "x") + str = str.replace("xx", "x") + str = str.replace("xx", "x") + str = str.replace("\nx", "\n") + # note that replacing x\n with \n would be incorrect, because + # x may be preceded by a backslash + + # March over the squashed version of the program, accumulating + # the line numbers of non-continued stmts, and determining + # whether & why the last stmt is a continuation. + continuation = C_NONE + level = lno = 0 # level is nesting level; lno is line number + self.goodlines = goodlines = [0] + push_good = goodlines.append + i, n = 0, len(str) + while i < n: + ch = str[i] + i = i + 1 + + # cases are checked in decreasing order of frequency + if ch == "x": + continue + + if ch == "\n": + lno = lno + 1 + if level == 0: + push_good(lno) + # else we're in an unclosed bracket structure + continue + + if ch == "(": + level = level + 1 + continue + + if ch == ")": + if level: + level = level - 1 + # else the program is invalid, but we can't complain + continue + + if ch == '"' or ch == "'": + # consume the string + quote = ch + if str[i - 1 : i + 2] == quote * 3: + quote = quote * 3 + w = len(quote) - 1 + i = i + w + while i < n: + ch = str[i] + i = i + 1 + + if ch == "x": + continue + + if str[i - 1 : i + w] == quote: + i = i + w + break + + if ch == "\n": + lno = lno + 1 + if w == 0: + # unterminated single-quoted string + if level == 0: + push_good(lno) + break + continue + + if ch == "\\": + assert i < n + if str[i] == "\n": + lno = lno + 1 + i = i + 1 + continue + + # else comment char or paren inside string + + else: + # didn't break out of the loop, so we're still + # inside a string + continuation = C_STRING + continue # with outer loop + + if ch == "#": + # consume the comment + i = str.find("\n", i) + assert i >= 0 + continue + + assert ch == "\\" + assert i < n + if str[i] == "\n": + lno = lno + 1 + if i + 1 == n: + continuation = C_BACKSLASH + i = i + 1 + + # The last stmt may be continued for all 3 reasons. + # String continuation takes precedence over bracket + # continuation, which beats backslash continuation. + if continuation != C_STRING and level > 0: + continuation = C_BRACKET + self.continuation = continuation + + # Push the final line number as a sentinel value, regardless of + # whether it's continued. + assert (continuation == C_NONE) == (goodlines[-1] == lno) + if goodlines[-1] != lno: + push_good(lno) + + def get_continuation_type(self): + self._study1() + return self.continuation + + # study1 was sufficient to determine the continuation status, + # but doing more requires looking at every character. study2 + # does this for the last interesting statement in the block. + # Creates: + # self.stmt_start, stmt_end + # slice indices of last interesting stmt + # self.lastch + # last non-whitespace character before optional trailing + # comment + # self.lastopenbracketpos + # if continuation is C_BRACKET, index of last open bracket + + def _study2(self): + _ws = string.whitespace + if self.study_level >= 2: + return + self._study1() + self.study_level = 2 + + # Set p and q to slice indices of last interesting stmt. + str, goodlines = self.str, self.goodlines + i = len(goodlines) - 1 + p = len(str) # index of newest line + while i: + assert p + # p is the index of the stmt at line number goodlines[i]. + # Move p back to the stmt at line number goodlines[i-1]. + q = p + for nothing in range(goodlines[i - 1], goodlines[i]): + # tricky: sets p to 0 if no preceding newline + p = str.rfind("\n", 0, p - 1) + 1 + # The stmt str[p:q] isn't a continuation, but may be blank + # or a non-indenting comment line. + if _junkre(str, p): + i = i - 1 + else: + break + if i == 0: + # nothing but junk! + assert p == 0 + q = p + self.stmt_start, self.stmt_end = p, q + + # Analyze this stmt, to find the last open bracket (if any) + # and last interesting character (if any). + lastch = "" + stack = [] # stack of open bracket indices + push_stack = stack.append + while p < q: + # suck up all except ()[]{}'"#\\ + m = _chew_ordinaryre(str, p, q) + if m: + # we skipped at least one boring char + newp = m.end() + # back up over totally boring whitespace + i = newp - 1 # index of last boring char + while i >= p and str[i] in " \t\n": + i = i - 1 + if i >= p: + lastch = str[i] + p = newp + if p >= q: + break + + ch = str[p] + + if ch in "([{": + push_stack(p) + lastch = ch + p = p + 1 + continue + + if ch in ")]}": + if stack: + del stack[-1] + lastch = ch + p = p + 1 + continue + + if ch == '"' or ch == "'": + # consume string + # Note that study1 did this with a Python loop, but + # we use a regexp here; the reason is speed in both + # cases; the string may be huge, but study1 pre-squashed + # strings to a couple of characters per line. study1 + # also needed to keep track of newlines, and we don't + # have to. + lastch = ch + p = _match_stringre(str, p, q).end() + continue + + if ch == "#": + # consume comment and trailing newline + p = str.find("\n", p, q) + 1 + assert p > 0 + continue + + assert ch == "\\" + p = p + 1 # beyond backslash + assert p < q + if str[p] != "\n": + # the program is invalid, but can't complain + lastch = ch + str[p] + p = p + 1 # beyond escaped char + + # end while p < q: + + self.lastch = lastch + if stack: + self.lastopenbracketpos = stack[-1] + + # Assuming continuation is C_BRACKET, return the number + # of spaces the next line should be indented. + + def compute_bracket_indent(self): + self._study2() + assert self.continuation == C_BRACKET + j = self.lastopenbracketpos + str = self.str + n = len(str) + origi = i = str.rfind("\n", 0, j) + 1 + j = j + 1 # one beyond open bracket + # find first list item; set i to start of its line + while j < n: + m = _itemre(str, j) + if m: + j = m.end() - 1 # index of first interesting char + extra = 0 + break + else: + # this line is junk; advance to next line + i = j = str.find("\n", j) + 1 + else: + # nothing interesting follows the bracket; + # reproduce the bracket line's indentation + a level + j = i = origi + while str[j] in " \t": + j = j + 1 + extra = self.indentwidth + return len(str[i:j].expandtabs(self.tabwidth)) + extra + + # Return number of physical lines in last stmt (whether or not + # it's an interesting stmt! this is intended to be called when + # continuation is C_BACKSLASH). + + def get_num_lines_in_stmt(self): + self._study1() + goodlines = self.goodlines + return goodlines[-1] - goodlines[-2] + + # Assuming continuation is C_BACKSLASH, return the number of spaces + # the next line should be indented. Also assuming the new line is + # the first one following the initial line of the stmt. + + def compute_backslash_indent(self): + self._study2() + assert self.continuation == C_BACKSLASH + str = self.str + i = self.stmt_start + while str[i] in " \t": + i = i + 1 + startpos = i + + # See whether the initial line starts an assignment stmt; i.e., + # look for an = operator + endpos = str.find("\n", startpos) + 1 + found = level = 0 + while i < endpos: + ch = str[i] + if ch in "([{": + level = level + 1 + i = i + 1 + elif ch in ")]}": + if level: + level = level - 1 + i = i + 1 + elif ch == '"' or ch == "'": + i = _match_stringre(str, i, endpos).end() + elif ch == "#": + break + elif ( + level == 0 + and ch == "=" + and (i == 0 or str[i - 1] not in "=<>!") + and str[i + 1] != "=" + ): + found = 1 + break + else: + i = i + 1 + + if found: + # found a legit =, but it may be the last interesting + # thing on the line + i = i + 1 # move beyond the = + found = re.match(r"\s*\\", str[i:endpos]) is None + + if not found: + # oh well ... settle for moving beyond the first chunk + # of non-whitespace chars + i = startpos + while str[i] not in " \t\n": + i = i + 1 + + return len(str[self.stmt_start : i].expandtabs(self.tabwidth)) + 1 + + # Return the leading whitespace on the initial line of the last + # interesting stmt. + + def get_base_indent_string(self): + self._study2() + i, n = self.stmt_start, self.stmt_end + j = i + str = self.str + while j < n and str[j] in " \t": + j = j + 1 + return str[i:j] + + # Did the last interesting stmt open a block? + + def is_block_opener(self): + self._study2() + return self.lastch == ":" + + # Did the last interesting stmt close a block? + + def is_block_closer(self): + self._study2() + return _closere(self.str, self.stmt_start) is not None + + # index of last open bracket ({[, or None if none + lastopenbracketpos = None + + def get_last_open_bracket_pos(self): + self._study2() + return self.lastopenbracketpos diff --git a/myenv/Lib/site-packages/pythonwin/pywin/idle/__init__.py b/myenv/Lib/site-packages/pythonwin/pywin/idle/__init__.py new file mode 100644 index 000000000..b175d7554 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/idle/__init__.py @@ -0,0 +1 @@ +# This file denotes the directory as a Python package. diff --git a/myenv/Lib/site-packages/pythonwin/pywin/mfc/__init__.py b/myenv/Lib/site-packages/pythonwin/pywin/mfc/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/myenv/Lib/site-packages/pythonwin/pywin/mfc/activex.py b/myenv/Lib/site-packages/pythonwin/pywin/mfc/activex.py new file mode 100644 index 000000000..596bbef5e --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/mfc/activex.py @@ -0,0 +1,86 @@ +"""Support for ActiveX control hosting in Pythonwin. +""" +import win32ui +import win32uiole + +from . import window + +# 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 + + +class Control(window.Wnd): + """An ActiveX control base class. A new class must be derived from both + this class and the Events class. See the demos for more details. + """ + + def __init__(self): + self.__dict__["_dispobj_"] = None + window.Wnd.__init__(self) + + def _GetControlCLSID(self): + return self.CLSID + + def _GetDispatchClass(self): + return self.default_interface + + def _GetEventMap(self): + return self.default_source._dispid_to_func_ + + def CreateControl(self, windowTitle, style, rect, parent, id, lic_string=None): + clsid = str(self._GetControlCLSID()) + self.__dict__["_obj_"] = win32ui.CreateControl( + clsid, windowTitle, style, rect, parent, id, None, False, lic_string + ) + klass = self._GetDispatchClass() + dispobj = klass(win32uiole.GetIDispatchForWindow(self._obj_)) + self.HookOleEvents() + self.__dict__["_dispobj_"] = dispobj + + def HookOleEvents(self): + dict = self._GetEventMap() + for dispid, methodName in dict.items(): + if hasattr(self, methodName): + self._obj_.HookOleEvent(getattr(self, methodName), dispid) + + def __getattr__(self, attr): + # Delegate attributes to the windows and the Dispatch object for this class + try: + return window.Wnd.__getattr__(self, attr) + except AttributeError: + pass + return getattr(self._dispobj_, attr) + + def __setattr__(self, attr, value): + if hasattr(self.__dict__, attr): + self.__dict__[attr] = value + return + try: + if self._dispobj_: + self._dispobj_.__setattr__(attr, value) + return + except AttributeError: + pass + self.__dict__[attr] = value + + +def MakeControlClass(controlClass, name=None): + """Given a CoClass in a generated .py file, this function will return a Class + object which can be used as an OCX control. + + This function is used when you do not want to handle any events from the OCX + control. If you need events, then you should derive a class from both the + activex.Control class and the CoClass + """ + if name is None: + name = controlClass.__name__ + return new_type("OCX" + name, (Control, controlClass), {}) + + +def MakeControlInstance(controlClass, name=None): + """As for MakeControlClass(), but returns an instance of the class.""" + return MakeControlClass(controlClass, name)() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/mfc/afxres.py b/myenv/Lib/site-packages/pythonwin/pywin/mfc/afxres.py new file mode 100644 index 000000000..249211ff3 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/mfc/afxres.py @@ -0,0 +1,501 @@ +# Generated by h2py from stdin +TCS_MULTILINE = 0x0200 +CBRS_ALIGN_LEFT = 0x1000 +CBRS_ALIGN_TOP = 0x2000 +CBRS_ALIGN_RIGHT = 0x4000 +CBRS_ALIGN_BOTTOM = 0x8000 +CBRS_ALIGN_ANY = 0xF000 +CBRS_BORDER_LEFT = 0x0100 +CBRS_BORDER_TOP = 0x0200 +CBRS_BORDER_RIGHT = 0x0400 +CBRS_BORDER_BOTTOM = 0x0800 +CBRS_BORDER_ANY = 0x0F00 +CBRS_TOOLTIPS = 0x0010 +CBRS_FLYBY = 0x0020 +CBRS_FLOAT_MULTI = 0x0040 +CBRS_BORDER_3D = 0x0080 +CBRS_HIDE_INPLACE = 0x0008 +CBRS_SIZE_DYNAMIC = 0x0004 +CBRS_SIZE_FIXED = 0x0002 +CBRS_FLOATING = 0x0001 +CBRS_GRIPPER = 0x00400000 +CBRS_ORIENT_HORZ = CBRS_ALIGN_TOP | CBRS_ALIGN_BOTTOM +CBRS_ORIENT_VERT = CBRS_ALIGN_LEFT | CBRS_ALIGN_RIGHT +CBRS_ORIENT_ANY = CBRS_ORIENT_HORZ | CBRS_ORIENT_VERT +CBRS_ALL = 0xFFFF +CBRS_NOALIGN = 0x00000000 +CBRS_LEFT = CBRS_ALIGN_LEFT | CBRS_BORDER_RIGHT +CBRS_TOP = CBRS_ALIGN_TOP | CBRS_BORDER_BOTTOM +CBRS_RIGHT = CBRS_ALIGN_RIGHT | CBRS_BORDER_LEFT +CBRS_BOTTOM = CBRS_ALIGN_BOTTOM | CBRS_BORDER_TOP +SBPS_NORMAL = 0x0000 +SBPS_NOBORDERS = 0x0100 +SBPS_POPOUT = 0x0200 +SBPS_OWNERDRAW = 0x1000 +SBPS_DISABLED = 0x04000000 +SBPS_STRETCH = 0x08000000 +ID_INDICATOR_EXT = 0xE700 +ID_INDICATOR_CAPS = 0xE701 +ID_INDICATOR_NUM = 0xE702 +ID_INDICATOR_SCRL = 0xE703 +ID_INDICATOR_OVR = 0xE704 +ID_INDICATOR_REC = 0xE705 +ID_INDICATOR_KANA = 0xE706 +ID_SEPARATOR = 0 +AFX_IDW_CONTROLBAR_FIRST = 0xE800 +AFX_IDW_CONTROLBAR_LAST = 0xE8FF +AFX_IDW_TOOLBAR = 0xE800 +AFX_IDW_STATUS_BAR = 0xE801 +AFX_IDW_PREVIEW_BAR = 0xE802 +AFX_IDW_RESIZE_BAR = 0xE803 +AFX_IDW_DOCKBAR_TOP = 0xE81B +AFX_IDW_DOCKBAR_LEFT = 0xE81C +AFX_IDW_DOCKBAR_RIGHT = 0xE81D +AFX_IDW_DOCKBAR_BOTTOM = 0xE81E +AFX_IDW_DOCKBAR_FLOAT = 0xE81F + + +def AFX_CONTROLBAR_MASK(nIDC): + return 1 << (nIDC - AFX_IDW_CONTROLBAR_FIRST) + + +AFX_IDW_PANE_FIRST = 0xE900 +AFX_IDW_PANE_LAST = 0xE9FF +AFX_IDW_HSCROLL_FIRST = 0xEA00 +AFX_IDW_VSCROLL_FIRST = 0xEA10 +AFX_IDW_SIZE_BOX = 0xEA20 +AFX_IDW_PANE_SAVE = 0xEA21 +AFX_IDS_APP_TITLE = 0xE000 +AFX_IDS_IDLEMESSAGE = 0xE001 +AFX_IDS_HELPMODEMESSAGE = 0xE002 +AFX_IDS_APP_TITLE_EMBEDDING = 0xE003 +AFX_IDS_COMPANY_NAME = 0xE004 +AFX_IDS_OBJ_TITLE_INPLACE = 0xE005 +ID_FILE_NEW = 0xE100 +ID_FILE_OPEN = 0xE101 +ID_FILE_CLOSE = 0xE102 +ID_FILE_SAVE = 0xE103 +ID_FILE_SAVE_AS = 0xE104 +ID_FILE_PAGE_SETUP = 0xE105 +ID_FILE_PRINT_SETUP = 0xE106 +ID_FILE_PRINT = 0xE107 +ID_FILE_PRINT_DIRECT = 0xE108 +ID_FILE_PRINT_PREVIEW = 0xE109 +ID_FILE_UPDATE = 0xE10A +ID_FILE_SAVE_COPY_AS = 0xE10B +ID_FILE_SEND_MAIL = 0xE10C +ID_FILE_MRU_FIRST = 0xE110 +ID_FILE_MRU_FILE1 = 0xE110 +ID_FILE_MRU_FILE2 = 0xE111 +ID_FILE_MRU_FILE3 = 0xE112 +ID_FILE_MRU_FILE4 = 0xE113 +ID_FILE_MRU_FILE5 = 0xE114 +ID_FILE_MRU_FILE6 = 0xE115 +ID_FILE_MRU_FILE7 = 0xE116 +ID_FILE_MRU_FILE8 = 0xE117 +ID_FILE_MRU_FILE9 = 0xE118 +ID_FILE_MRU_FILE10 = 0xE119 +ID_FILE_MRU_FILE11 = 0xE11A +ID_FILE_MRU_FILE12 = 0xE11B +ID_FILE_MRU_FILE13 = 0xE11C +ID_FILE_MRU_FILE14 = 0xE11D +ID_FILE_MRU_FILE15 = 0xE11E +ID_FILE_MRU_FILE16 = 0xE11F +ID_FILE_MRU_LAST = 0xE11F +ID_EDIT_CLEAR = 0xE120 +ID_EDIT_CLEAR_ALL = 0xE121 +ID_EDIT_COPY = 0xE122 +ID_EDIT_CUT = 0xE123 +ID_EDIT_FIND = 0xE124 +ID_EDIT_PASTE = 0xE125 +ID_EDIT_PASTE_LINK = 0xE126 +ID_EDIT_PASTE_SPECIAL = 0xE127 +ID_EDIT_REPEAT = 0xE128 +ID_EDIT_REPLACE = 0xE129 +ID_EDIT_SELECT_ALL = 0xE12A +ID_EDIT_UNDO = 0xE12B +ID_EDIT_REDO = 0xE12C +ID_WINDOW_NEW = 0xE130 +ID_WINDOW_ARRANGE = 0xE131 +ID_WINDOW_CASCADE = 0xE132 +ID_WINDOW_TILE_HORZ = 0xE133 +ID_WINDOW_TILE_VERT = 0xE134 +ID_WINDOW_SPLIT = 0xE135 +AFX_IDM_WINDOW_FIRST = 0xE130 +AFX_IDM_WINDOW_LAST = 0xE13F +AFX_IDM_FIRST_MDICHILD = 0xFF00 +ID_APP_ABOUT = 0xE140 +ID_APP_EXIT = 0xE141 +ID_HELP_INDEX = 0xE142 +ID_HELP_FINDER = 0xE143 +ID_HELP_USING = 0xE144 +ID_CONTEXT_HELP = 0xE145 +ID_HELP = 0xE146 +ID_DEFAULT_HELP = 0xE147 +ID_NEXT_PANE = 0xE150 +ID_PREV_PANE = 0xE151 +ID_FORMAT_FONT = 0xE160 +ID_OLE_INSERT_NEW = 0xE200 +ID_OLE_EDIT_LINKS = 0xE201 +ID_OLE_EDIT_CONVERT = 0xE202 +ID_OLE_EDIT_CHANGE_ICON = 0xE203 +ID_OLE_EDIT_PROPERTIES = 0xE204 +ID_OLE_VERB_FIRST = 0xE210 +ID_OLE_VERB_LAST = 0xE21F +AFX_ID_PREVIEW_CLOSE = 0xE300 +AFX_ID_PREVIEW_NUMPAGE = 0xE301 +AFX_ID_PREVIEW_NEXT = 0xE302 +AFX_ID_PREVIEW_PREV = 0xE303 +AFX_ID_PREVIEW_PRINT = 0xE304 +AFX_ID_PREVIEW_ZOOMIN = 0xE305 +AFX_ID_PREVIEW_ZOOMOUT = 0xE306 +ID_VIEW_TOOLBAR = 0xE800 +ID_VIEW_STATUS_BAR = 0xE801 +ID_RECORD_FIRST = 0xE900 +ID_RECORD_LAST = 0xE901 +ID_RECORD_NEXT = 0xE902 +ID_RECORD_PREV = 0xE903 +IDC_STATIC = -1 +AFX_IDS_SCFIRST = 0xEF00 +AFX_IDS_SCSIZE = 0xEF00 +AFX_IDS_SCMOVE = 0xEF01 +AFX_IDS_SCMINIMIZE = 0xEF02 +AFX_IDS_SCMAXIMIZE = 0xEF03 +AFX_IDS_SCNEXTWINDOW = 0xEF04 +AFX_IDS_SCPREVWINDOW = 0xEF05 +AFX_IDS_SCCLOSE = 0xEF06 +AFX_IDS_SCRESTORE = 0xEF12 +AFX_IDS_SCTASKLIST = 0xEF13 +AFX_IDS_MDICHILD = 0xEF1F +AFX_IDS_DESKACCESSORY = 0xEFDA +AFX_IDS_OPENFILE = 0xF000 +AFX_IDS_SAVEFILE = 0xF001 +AFX_IDS_ALLFILTER = 0xF002 +AFX_IDS_UNTITLED = 0xF003 +AFX_IDS_SAVEFILECOPY = 0xF004 +AFX_IDS_PREVIEW_CLOSE = 0xF005 +AFX_IDS_UNNAMED_FILE = 0xF006 +AFX_IDS_ABOUT = 0xF010 +AFX_IDS_HIDE = 0xF011 +AFX_IDP_NO_ERROR_AVAILABLE = 0xF020 +AFX_IDS_NOT_SUPPORTED_EXCEPTION = 0xF021 +AFX_IDS_RESOURCE_EXCEPTION = 0xF022 +AFX_IDS_MEMORY_EXCEPTION = 0xF023 +AFX_IDS_USER_EXCEPTION = 0xF024 +AFX_IDS_PRINTONPORT = 0xF040 +AFX_IDS_ONEPAGE = 0xF041 +AFX_IDS_TWOPAGE = 0xF042 +AFX_IDS_PRINTPAGENUM = 0xF043 +AFX_IDS_PREVIEWPAGEDESC = 0xF044 +AFX_IDS_PRINTDEFAULTEXT = 0xF045 +AFX_IDS_PRINTDEFAULT = 0xF046 +AFX_IDS_PRINTFILTER = 0xF047 +AFX_IDS_PRINTCAPTION = 0xF048 +AFX_IDS_PRINTTOFILE = 0xF049 +AFX_IDS_OBJECT_MENUITEM = 0xF080 +AFX_IDS_EDIT_VERB = 0xF081 +AFX_IDS_ACTIVATE_VERB = 0xF082 +AFX_IDS_CHANGE_LINK = 0xF083 +AFX_IDS_AUTO = 0xF084 +AFX_IDS_MANUAL = 0xF085 +AFX_IDS_FROZEN = 0xF086 +AFX_IDS_ALL_FILES = 0xF087 +AFX_IDS_SAVE_MENU = 0xF088 +AFX_IDS_UPDATE_MENU = 0xF089 +AFX_IDS_SAVE_AS_MENU = 0xF08A +AFX_IDS_SAVE_COPY_AS_MENU = 0xF08B +AFX_IDS_EXIT_MENU = 0xF08C +AFX_IDS_UPDATING_ITEMS = 0xF08D +AFX_IDS_METAFILE_FORMAT = 0xF08E +AFX_IDS_DIB_FORMAT = 0xF08F +AFX_IDS_BITMAP_FORMAT = 0xF090 +AFX_IDS_LINKSOURCE_FORMAT = 0xF091 +AFX_IDS_EMBED_FORMAT = 0xF092 +AFX_IDS_PASTELINKEDTYPE = 0xF094 +AFX_IDS_UNKNOWNTYPE = 0xF095 +AFX_IDS_RTF_FORMAT = 0xF096 +AFX_IDS_TEXT_FORMAT = 0xF097 +AFX_IDS_INVALID_CURRENCY = 0xF098 +AFX_IDS_INVALID_DATETIME = 0xF099 +AFX_IDS_INVALID_DATETIMESPAN = 0xF09A +AFX_IDP_INVALID_FILENAME = 0xF100 +AFX_IDP_FAILED_TO_OPEN_DOC = 0xF101 +AFX_IDP_FAILED_TO_SAVE_DOC = 0xF102 +AFX_IDP_ASK_TO_SAVE = 0xF103 +AFX_IDP_FAILED_TO_CREATE_DOC = 0xF104 +AFX_IDP_FILE_TOO_LARGE = 0xF105 +AFX_IDP_FAILED_TO_START_PRINT = 0xF106 +AFX_IDP_FAILED_TO_LAUNCH_HELP = 0xF107 +AFX_IDP_INTERNAL_FAILURE = 0xF108 +AFX_IDP_COMMAND_FAILURE = 0xF109 +AFX_IDP_FAILED_MEMORY_ALLOC = 0xF10A +AFX_IDP_PARSE_INT = 0xF110 +AFX_IDP_PARSE_REAL = 0xF111 +AFX_IDP_PARSE_INT_RANGE = 0xF112 +AFX_IDP_PARSE_REAL_RANGE = 0xF113 +AFX_IDP_PARSE_STRING_SIZE = 0xF114 +AFX_IDP_PARSE_RADIO_BUTTON = 0xF115 +AFX_IDP_PARSE_BYTE = 0xF116 +AFX_IDP_PARSE_UINT = 0xF117 +AFX_IDP_PARSE_DATETIME = 0xF118 +AFX_IDP_PARSE_CURRENCY = 0xF119 +AFX_IDP_FAILED_INVALID_FORMAT = 0xF120 +AFX_IDP_FAILED_INVALID_PATH = 0xF121 +AFX_IDP_FAILED_DISK_FULL = 0xF122 +AFX_IDP_FAILED_ACCESS_READ = 0xF123 +AFX_IDP_FAILED_ACCESS_WRITE = 0xF124 +AFX_IDP_FAILED_IO_ERROR_READ = 0xF125 +AFX_IDP_FAILED_IO_ERROR_WRITE = 0xF126 +AFX_IDP_STATIC_OBJECT = 0xF180 +AFX_IDP_FAILED_TO_CONNECT = 0xF181 +AFX_IDP_SERVER_BUSY = 0xF182 +AFX_IDP_BAD_VERB = 0xF183 +AFX_IDP_FAILED_TO_NOTIFY = 0xF185 +AFX_IDP_FAILED_TO_LAUNCH = 0xF186 +AFX_IDP_ASK_TO_UPDATE = 0xF187 +AFX_IDP_FAILED_TO_UPDATE = 0xF188 +AFX_IDP_FAILED_TO_REGISTER = 0xF189 +AFX_IDP_FAILED_TO_AUTO_REGISTER = 0xF18A +AFX_IDP_FAILED_TO_CONVERT = 0xF18B +AFX_IDP_GET_NOT_SUPPORTED = 0xF18C +AFX_IDP_SET_NOT_SUPPORTED = 0xF18D +AFX_IDP_ASK_TO_DISCARD = 0xF18E +AFX_IDP_FAILED_TO_CREATE = 0xF18F +AFX_IDP_FAILED_MAPI_LOAD = 0xF190 +AFX_IDP_INVALID_MAPI_DLL = 0xF191 +AFX_IDP_FAILED_MAPI_SEND = 0xF192 +AFX_IDP_FILE_NONE = 0xF1A0 +AFX_IDP_FILE_GENERIC = 0xF1A1 +AFX_IDP_FILE_NOT_FOUND = 0xF1A2 +AFX_IDP_FILE_BAD_PATH = 0xF1A3 +AFX_IDP_FILE_TOO_MANY_OPEN = 0xF1A4 +AFX_IDP_FILE_ACCESS_DENIED = 0xF1A5 +AFX_IDP_FILE_INVALID_FILE = 0xF1A6 +AFX_IDP_FILE_REMOVE_CURRENT = 0xF1A7 +AFX_IDP_FILE_DIR_FULL = 0xF1A8 +AFX_IDP_FILE_BAD_SEEK = 0xF1A9 +AFX_IDP_FILE_HARD_IO = 0xF1AA +AFX_IDP_FILE_SHARING = 0xF1AB +AFX_IDP_FILE_LOCKING = 0xF1AC +AFX_IDP_FILE_DISKFULL = 0xF1AD +AFX_IDP_FILE_EOF = 0xF1AE +AFX_IDP_ARCH_NONE = 0xF1B0 +AFX_IDP_ARCH_GENERIC = 0xF1B1 +AFX_IDP_ARCH_READONLY = 0xF1B2 +AFX_IDP_ARCH_ENDOFFILE = 0xF1B3 +AFX_IDP_ARCH_WRITEONLY = 0xF1B4 +AFX_IDP_ARCH_BADINDEX = 0xF1B5 +AFX_IDP_ARCH_BADCLASS = 0xF1B6 +AFX_IDP_ARCH_BADSCHEMA = 0xF1B7 +AFX_IDS_OCC_SCALEUNITS_PIXELS = 0xF1C0 +AFX_IDS_STATUS_FONT = 0xF230 +AFX_IDS_TOOLTIP_FONT = 0xF231 +AFX_IDS_UNICODE_FONT = 0xF232 +AFX_IDS_MINI_FONT = 0xF233 +AFX_IDP_SQL_FIRST = 0xF280 +AFX_IDP_SQL_CONNECT_FAIL = 0xF281 +AFX_IDP_SQL_RECORDSET_FORWARD_ONLY = 0xF282 +AFX_IDP_SQL_EMPTY_COLUMN_LIST = 0xF283 +AFX_IDP_SQL_FIELD_SCHEMA_MISMATCH = 0xF284 +AFX_IDP_SQL_ILLEGAL_MODE = 0xF285 +AFX_IDP_SQL_MULTIPLE_ROWS_AFFECTED = 0xF286 +AFX_IDP_SQL_NO_CURRENT_RECORD = 0xF287 +AFX_IDP_SQL_NO_ROWS_AFFECTED = 0xF288 +AFX_IDP_SQL_RECORDSET_READONLY = 0xF289 +AFX_IDP_SQL_SQL_NO_TOTAL = 0xF28A +AFX_IDP_SQL_ODBC_LOAD_FAILED = 0xF28B +AFX_IDP_SQL_DYNASET_NOT_SUPPORTED = 0xF28C +AFX_IDP_SQL_SNAPSHOT_NOT_SUPPORTED = 0xF28D +AFX_IDP_SQL_API_CONFORMANCE = 0xF28E +AFX_IDP_SQL_SQL_CONFORMANCE = 0xF28F +AFX_IDP_SQL_NO_DATA_FOUND = 0xF290 +AFX_IDP_SQL_ROW_UPDATE_NOT_SUPPORTED = 0xF291 +AFX_IDP_SQL_ODBC_V2_REQUIRED = 0xF292 +AFX_IDP_SQL_NO_POSITIONED_UPDATES = 0xF293 +AFX_IDP_SQL_LOCK_MODE_NOT_SUPPORTED = 0xF294 +AFX_IDP_SQL_DATA_TRUNCATED = 0xF295 +AFX_IDP_SQL_ROW_FETCH = 0xF296 +AFX_IDP_SQL_INCORRECT_ODBC = 0xF297 +AFX_IDP_SQL_UPDATE_DELETE_FAILED = 0xF298 +AFX_IDP_SQL_DYNAMIC_CURSOR_NOT_SUPPORTED = 0xF299 +AFX_IDP_DAO_FIRST = 0xF2A0 +AFX_IDP_DAO_ENGINE_INITIALIZATION = 0xF2A0 +AFX_IDP_DAO_DFX_BIND = 0xF2A1 +AFX_IDP_DAO_OBJECT_NOT_OPEN = 0xF2A2 +AFX_IDP_DAO_ROWTOOSHORT = 0xF2A3 +AFX_IDP_DAO_BADBINDINFO = 0xF2A4 +AFX_IDP_DAO_COLUMNUNAVAILABLE = 0xF2A5 +AFX_IDC_LISTBOX = 100 +AFX_IDC_CHANGE = 101 +AFX_IDC_PRINT_DOCNAME = 201 +AFX_IDC_PRINT_PRINTERNAME = 202 +AFX_IDC_PRINT_PORTNAME = 203 +AFX_IDC_PRINT_PAGENUM = 204 +ID_APPLY_NOW = 0x3021 +ID_WIZBACK = 0x3023 +ID_WIZNEXT = 0x3024 +ID_WIZFINISH = 0x3025 +AFX_IDC_TAB_CONTROL = 0x3020 +AFX_IDD_FILEOPEN = 28676 +AFX_IDD_FILESAVE = 28677 +AFX_IDD_FONT = 28678 +AFX_IDD_COLOR = 28679 +AFX_IDD_PRINT = 28680 +AFX_IDD_PRINTSETUP = 28681 +AFX_IDD_FIND = 28682 +AFX_IDD_REPLACE = 28683 +AFX_IDD_NEWTYPEDLG = 30721 +AFX_IDD_PRINTDLG = 30722 +AFX_IDD_PREVIEW_TOOLBAR = 30723 +AFX_IDD_PREVIEW_SHORTTOOLBAR = 30731 +AFX_IDD_INSERTOBJECT = 30724 +AFX_IDD_CHANGEICON = 30725 +AFX_IDD_CONVERT = 30726 +AFX_IDD_PASTESPECIAL = 30727 +AFX_IDD_EDITLINKS = 30728 +AFX_IDD_FILEBROWSE = 30729 +AFX_IDD_BUSY = 30730 +AFX_IDD_OBJECTPROPERTIES = 30732 +AFX_IDD_CHANGESOURCE = 30733 +AFX_IDC_CONTEXTHELP = 30977 +AFX_IDC_MAGNIFY = 30978 +AFX_IDC_SMALLARROWS = 30979 +AFX_IDC_HSPLITBAR = 30980 +AFX_IDC_VSPLITBAR = 30981 +AFX_IDC_NODROPCRSR = 30982 +AFX_IDC_TRACKNWSE = 30983 +AFX_IDC_TRACKNESW = 30984 +AFX_IDC_TRACKNS = 30985 +AFX_IDC_TRACKWE = 30986 +AFX_IDC_TRACK4WAY = 30987 +AFX_IDC_MOVE4WAY = 30988 +AFX_IDB_MINIFRAME_MENU = 30994 +AFX_IDB_CHECKLISTBOX_NT = 30995 +AFX_IDB_CHECKLISTBOX_95 = 30996 +AFX_IDR_PREVIEW_ACCEL = 30997 +AFX_IDI_STD_MDIFRAME = 31233 +AFX_IDI_STD_FRAME = 31234 +AFX_IDC_FONTPROP = 1000 +AFX_IDC_FONTNAMES = 1001 +AFX_IDC_FONTSTYLES = 1002 +AFX_IDC_FONTSIZES = 1003 +AFX_IDC_STRIKEOUT = 1004 +AFX_IDC_UNDERLINE = 1005 +AFX_IDC_SAMPLEBOX = 1006 +AFX_IDC_COLOR_BLACK = 1100 +AFX_IDC_COLOR_WHITE = 1101 +AFX_IDC_COLOR_RED = 1102 +AFX_IDC_COLOR_GREEN = 1103 +AFX_IDC_COLOR_BLUE = 1104 +AFX_IDC_COLOR_YELLOW = 1105 +AFX_IDC_COLOR_MAGENTA = 1106 +AFX_IDC_COLOR_CYAN = 1107 +AFX_IDC_COLOR_GRAY = 1108 +AFX_IDC_COLOR_LIGHTGRAY = 1109 +AFX_IDC_COLOR_DARKRED = 1110 +AFX_IDC_COLOR_DARKGREEN = 1111 +AFX_IDC_COLOR_DARKBLUE = 1112 +AFX_IDC_COLOR_LIGHTBROWN = 1113 +AFX_IDC_COLOR_DARKMAGENTA = 1114 +AFX_IDC_COLOR_DARKCYAN = 1115 +AFX_IDC_COLORPROP = 1116 +AFX_IDC_SYSTEMCOLORS = 1117 +AFX_IDC_PROPNAME = 1201 +AFX_IDC_PICTURE = 1202 +AFX_IDC_BROWSE = 1203 +AFX_IDC_CLEAR = 1204 +AFX_IDD_PROPPAGE_COLOR = 32257 +AFX_IDD_PROPPAGE_FONT = 32258 +AFX_IDD_PROPPAGE_PICTURE = 32259 +AFX_IDB_TRUETYPE = 32384 +AFX_IDS_PROPPAGE_UNKNOWN = 0xFE01 +AFX_IDS_COLOR_DESKTOP = 0xFE04 +AFX_IDS_COLOR_APPWORKSPACE = 0xFE05 +AFX_IDS_COLOR_WNDBACKGND = 0xFE06 +AFX_IDS_COLOR_WNDTEXT = 0xFE07 +AFX_IDS_COLOR_MENUBAR = 0xFE08 +AFX_IDS_COLOR_MENUTEXT = 0xFE09 +AFX_IDS_COLOR_ACTIVEBAR = 0xFE0A +AFX_IDS_COLOR_INACTIVEBAR = 0xFE0B +AFX_IDS_COLOR_ACTIVETEXT = 0xFE0C +AFX_IDS_COLOR_INACTIVETEXT = 0xFE0D +AFX_IDS_COLOR_ACTIVEBORDER = 0xFE0E +AFX_IDS_COLOR_INACTIVEBORDER = 0xFE0F +AFX_IDS_COLOR_WNDFRAME = 0xFE10 +AFX_IDS_COLOR_SCROLLBARS = 0xFE11 +AFX_IDS_COLOR_BTNFACE = 0xFE12 +AFX_IDS_COLOR_BTNSHADOW = 0xFE13 +AFX_IDS_COLOR_BTNTEXT = 0xFE14 +AFX_IDS_COLOR_BTNHIGHLIGHT = 0xFE15 +AFX_IDS_COLOR_DISABLEDTEXT = 0xFE16 +AFX_IDS_COLOR_HIGHLIGHT = 0xFE17 +AFX_IDS_COLOR_HIGHLIGHTTEXT = 0xFE18 +AFX_IDS_REGULAR = 0xFE19 +AFX_IDS_BOLD = 0xFE1A +AFX_IDS_ITALIC = 0xFE1B +AFX_IDS_BOLDITALIC = 0xFE1C +AFX_IDS_SAMPLETEXT = 0xFE1D +AFX_IDS_DISPLAYSTRING_FONT = 0xFE1E +AFX_IDS_DISPLAYSTRING_COLOR = 0xFE1F +AFX_IDS_DISPLAYSTRING_PICTURE = 0xFE20 +AFX_IDS_PICTUREFILTER = 0xFE21 +AFX_IDS_PICTYPE_UNKNOWN = 0xFE22 +AFX_IDS_PICTYPE_NONE = 0xFE23 +AFX_IDS_PICTYPE_BITMAP = 0xFE24 +AFX_IDS_PICTYPE_METAFILE = 0xFE25 +AFX_IDS_PICTYPE_ICON = 0xFE26 +AFX_IDS_COLOR_PPG = 0xFE28 +AFX_IDS_COLOR_PPG_CAPTION = 0xFE29 +AFX_IDS_FONT_PPG = 0xFE2A +AFX_IDS_FONT_PPG_CAPTION = 0xFE2B +AFX_IDS_PICTURE_PPG = 0xFE2C +AFX_IDS_PICTURE_PPG_CAPTION = 0xFE2D +AFX_IDS_PICTUREBROWSETITLE = 0xFE30 +AFX_IDS_BORDERSTYLE_0 = 0xFE31 +AFX_IDS_BORDERSTYLE_1 = 0xFE32 +AFX_IDS_VERB_EDIT = 0xFE40 +AFX_IDS_VERB_PROPERTIES = 0xFE41 +AFX_IDP_PICTURECANTOPEN = 0xFE83 +AFX_IDP_PICTURECANTLOAD = 0xFE84 +AFX_IDP_PICTURETOOLARGE = 0xFE85 +AFX_IDP_PICTUREREADFAILED = 0xFE86 +AFX_IDP_E_ILLEGALFUNCTIONCALL = 0xFEA0 +AFX_IDP_E_OVERFLOW = 0xFEA1 +AFX_IDP_E_OUTOFMEMORY = 0xFEA2 +AFX_IDP_E_DIVISIONBYZERO = 0xFEA3 +AFX_IDP_E_OUTOFSTRINGSPACE = 0xFEA4 +AFX_IDP_E_OUTOFSTACKSPACE = 0xFEA5 +AFX_IDP_E_BADFILENAMEORNUMBER = 0xFEA6 +AFX_IDP_E_FILENOTFOUND = 0xFEA7 +AFX_IDP_E_BADFILEMODE = 0xFEA8 +AFX_IDP_E_FILEALREADYOPEN = 0xFEA9 +AFX_IDP_E_DEVICEIOERROR = 0xFEAA +AFX_IDP_E_FILEALREADYEXISTS = 0xFEAB +AFX_IDP_E_BADRECORDLENGTH = 0xFEAC +AFX_IDP_E_DISKFULL = 0xFEAD +AFX_IDP_E_BADRECORDNUMBER = 0xFEAE +AFX_IDP_E_BADFILENAME = 0xFEAF +AFX_IDP_E_TOOMANYFILES = 0xFEB0 +AFX_IDP_E_DEVICEUNAVAILABLE = 0xFEB1 +AFX_IDP_E_PERMISSIONDENIED = 0xFEB2 +AFX_IDP_E_DISKNOTREADY = 0xFEB3 +AFX_IDP_E_PATHFILEACCESSERROR = 0xFEB4 +AFX_IDP_E_PATHNOTFOUND = 0xFEB5 +AFX_IDP_E_INVALIDPATTERNSTRING = 0xFEB6 +AFX_IDP_E_INVALIDUSEOFNULL = 0xFEB7 +AFX_IDP_E_INVALIDFILEFORMAT = 0xFEB8 +AFX_IDP_E_INVALIDPROPERTYVALUE = 0xFEB9 +AFX_IDP_E_INVALIDPROPERTYARRAYINDEX = 0xFEBA +AFX_IDP_E_SETNOTSUPPORTEDATRUNTIME = 0xFEBB +AFX_IDP_E_SETNOTSUPPORTED = 0xFEBC +AFX_IDP_E_NEEDPROPERTYARRAYINDEX = 0xFEBD +AFX_IDP_E_SETNOTPERMITTED = 0xFEBE +AFX_IDP_E_GETNOTSUPPORTEDATRUNTIME = 0xFEBF +AFX_IDP_E_GETNOTSUPPORTED = 0xFEC0 +AFX_IDP_E_PROPERTYNOTFOUND = 0xFEC1 +AFX_IDP_E_INVALIDCLIPBOARDFORMAT = 0xFEC2 +AFX_IDP_E_INVALIDPICTURE = 0xFEC3 +AFX_IDP_E_PRINTERERROR = 0xFEC4 +AFX_IDP_E_CANTSAVEFILETOTEMP = 0xFEC5 +AFX_IDP_E_SEARCHTEXTNOTFOUND = 0xFEC6 +AFX_IDP_E_REPLACEMENTSTOOLONG = 0xFEC7 diff --git a/myenv/Lib/site-packages/pythonwin/pywin/mfc/dialog.py b/myenv/Lib/site-packages/pythonwin/pywin/mfc/dialog.py new file mode 100644 index 000000000..25fe13635 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/mfc/dialog.py @@ -0,0 +1,278 @@ +""" \ +Base class for Dialogs. Also contains a few useful utility functions +""" +# dialog.py +# Python class for Dialog Boxes in PythonWin. + +import win32con +import win32ui + +# sob - 2to3 doesn't see this as a relative import :( +from pywin.mfc import window + + +def dllFromDll(dllid): + "given a 'dll' (maybe a dll, filename, etc), return a DLL object" + if dllid == None: + return None + elif type("") == type(dllid): + return win32ui.LoadLibrary(dllid) + else: + try: + dllid.GetFileName() + except AttributeError: + raise TypeError("DLL parameter must be None, a filename or a dll object") + return dllid + + +class Dialog(window.Wnd): + "Base class for a dialog" + + def __init__(self, id, dllid=None): + """id is the resource ID, or a template + dllid may be None, a dll object, or a string with a dll name""" + # must take a reference to the DLL until InitDialog. + self.dll = dllFromDll(dllid) + if type(id) == type([]): # a template + dlg = win32ui.CreateDialogIndirect(id) + else: + dlg = win32ui.CreateDialog(id, self.dll) + window.Wnd.__init__(self, dlg) + self.HookCommands() + self.bHaveInit = None + + def HookCommands(self): + pass + + def OnAttachedObjectDeath(self): + self.data = self._obj_.data + window.Wnd.OnAttachedObjectDeath(self) + + # provide virtuals. + def OnOK(self): + self._obj_.OnOK() + + def OnCancel(self): + self._obj_.OnCancel() + + def OnInitDialog(self): + self.bHaveInit = 1 + if self._obj_.data: + self._obj_.UpdateData(0) + return 1 # I did NOT set focus to a child window. + + def OnDestroy(self, msg): + self.dll = None # theoretically not needed if object destructs normally. + + # DDX support + def AddDDX(self, *args): + self._obj_.datalist.append(args) + + # Make a dialog object look like a dictionary for the DDX support + def __bool__(self): + return True + + def __len__(self): + return len(self.data) + + def __getitem__(self, key): + return self.data[key] + + def __setitem__(self, key, item): + self._obj_.data[key] = item # self.UpdateData(0) + + def keys(self): + return list(self.data.keys()) + + def items(self): + return list(self.data.items()) + + def values(self): + return list(self.data.values()) + + # XXX - needs py3k work! + def has_key(self, key): + return key in self.data + + +class PrintDialog(Dialog): + "Base class for a print dialog" + + def __init__( + self, + pInfo, + dlgID, + printSetupOnly=0, + flags=( + win32ui.PD_ALLPAGES + | win32ui.PD_USEDEVMODECOPIES + | win32ui.PD_NOPAGENUMS + | win32ui.PD_HIDEPRINTTOFILE + | win32ui.PD_NOSELECTION + ), + parent=None, + dllid=None, + ): + self.dll = dllFromDll(dllid) + if type(dlgID) == type([]): # a template + raise TypeError("dlgID parameter must be an integer resource ID") + dlg = win32ui.CreatePrintDialog(dlgID, printSetupOnly, flags, parent, self.dll) + window.Wnd.__init__(self, dlg) + self.HookCommands() + self.bHaveInit = None + self.pInfo = pInfo + # init values (if PrintSetup is called, values still available) + flags = pInfo.GetFlags() + self["toFile"] = flags & win32ui.PD_PRINTTOFILE != 0 + self["direct"] = pInfo.GetDirect() + self["preview"] = pInfo.GetPreview() + self["continuePrinting"] = pInfo.GetContinuePrinting() + self["curPage"] = pInfo.GetCurPage() + self["numPreviewPages"] = pInfo.GetNumPreviewPages() + self["userData"] = pInfo.GetUserData() + self["draw"] = pInfo.GetDraw() + self["pageDesc"] = pInfo.GetPageDesc() + self["minPage"] = pInfo.GetMinPage() + self["maxPage"] = pInfo.GetMaxPage() + self["offsetPage"] = pInfo.GetOffsetPage() + self["fromPage"] = pInfo.GetFromPage() + self["toPage"] = pInfo.GetToPage() + # these values updated after OnOK + self["copies"] = 0 + self["deviceName"] = "" + self["driverName"] = "" + self["printAll"] = 0 + self["printCollate"] = 0 + self["printRange"] = 0 + self["printSelection"] = 0 + + def OnInitDialog(self): + self.pInfo.CreatePrinterDC() # This also sets the hDC of the pInfo structure. + return self._obj_.OnInitDialog() + + def OnCancel(self): + del self.pInfo + + def OnOK(self): + """DoModal has finished. Can now access the users choices""" + self._obj_.OnOK() + pInfo = self.pInfo + # user values + flags = pInfo.GetFlags() + self["toFile"] = flags & win32ui.PD_PRINTTOFILE != 0 + self["direct"] = pInfo.GetDirect() + self["preview"] = pInfo.GetPreview() + self["continuePrinting"] = pInfo.GetContinuePrinting() + self["curPage"] = pInfo.GetCurPage() + self["numPreviewPages"] = pInfo.GetNumPreviewPages() + self["userData"] = pInfo.GetUserData() + self["draw"] = pInfo.GetDraw() + self["pageDesc"] = pInfo.GetPageDesc() + self["minPage"] = pInfo.GetMinPage() + self["maxPage"] = pInfo.GetMaxPage() + self["offsetPage"] = pInfo.GetOffsetPage() + self["fromPage"] = pInfo.GetFromPage() + self["toPage"] = pInfo.GetToPage() + self["copies"] = pInfo.GetCopies() + self["deviceName"] = pInfo.GetDeviceName() + self["driverName"] = pInfo.GetDriverName() + self["printAll"] = pInfo.PrintAll() + self["printCollate"] = pInfo.PrintCollate() + self["printRange"] = pInfo.PrintRange() + self["printSelection"] = pInfo.PrintSelection() + del self.pInfo + + +class PropertyPage(Dialog): + "Base class for a Property Page" + + def __init__(self, id, dllid=None, caption=0): + """id is the resource ID + dllid may be None, a dll object, or a string with a dll name""" + + self.dll = dllFromDll(dllid) + if self.dll: + oldRes = win32ui.SetResource(self.dll) + if type(id) == type([]): + dlg = win32ui.CreatePropertyPageIndirect(id) + else: + dlg = win32ui.CreatePropertyPage(id, caption) + if self.dll: + win32ui.SetResource(oldRes) + # dont call dialog init! + window.Wnd.__init__(self, dlg) + self.HookCommands() + + +class PropertySheet(window.Wnd): + def __init__(self, caption, dll=None, pageList=None): # parent=None, style,etc): + "Initialize a property sheet. pageList is a list of ID's" + # must take a reference to the DLL until InitDialog. + self.dll = dllFromDll(dll) + self.sheet = win32ui.CreatePropertySheet(caption) + window.Wnd.__init__(self, self.sheet) + if not pageList is None: + self.AddPage(pageList) + + def OnInitDialog(self): + return self._obj_.OnInitDialog() + + def DoModal(self): + if self.dll: + oldRes = win32ui.SetResource(self.dll) + rc = self.sheet.DoModal() + if self.dll: + win32ui.SetResource(oldRes) + return rc + + def AddPage(self, pages): + if self.dll: + oldRes = win32ui.SetResource(self.dll) + try: # try list style access + pages[0] + isSeq = 1 + except (TypeError, KeyError): + isSeq = 0 + if isSeq: + for page in pages: + self.DoAddSinglePage(page) + else: + self.DoAddSinglePage(pages) + if self.dll: + win32ui.SetResource(oldRes) + + def DoAddSinglePage(self, page): + "Page may be page, or int ID. Assumes DLL setup" + if type(page) == type(0): + self.sheet.AddPage(win32ui.CreatePropertyPage(page)) + else: + self.sheet.AddPage(page) + + +# define some app utility functions. +def GetSimpleInput(prompt, defValue="", title=None): + """displays a dialog, and returns a string, or None if cancelled. + args prompt, defValue='', title=main frames title""" + # uses a simple dialog to return a string object. + if title is None: + title = win32ui.GetMainFrame().GetWindowText() + # 2to3 insists on converting 'Dialog.__init__' to 'tkinter.dialog...' + DlgBaseClass = Dialog + + class DlgSimpleInput(DlgBaseClass): + def __init__(self, prompt, defValue, title): + self.title = title + DlgBaseClass.__init__(self, win32ui.IDD_SIMPLE_INPUT) + self.AddDDX(win32ui.IDC_EDIT1, "result") + self.AddDDX(win32ui.IDC_PROMPT1, "prompt") + self._obj_.data["result"] = defValue + self._obj_.data["prompt"] = prompt + + def OnInitDialog(self): + self.SetWindowText(self.title) + return DlgBaseClass.OnInitDialog(self) + + dlg = DlgSimpleInput(prompt, defValue, title) + if dlg.DoModal() != win32con.IDOK: + return None + return dlg["result"] diff --git a/myenv/Lib/site-packages/pythonwin/pywin/mfc/docview.py b/myenv/Lib/site-packages/pythonwin/pywin/mfc/docview.py new file mode 100644 index 000000000..7da91b460 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/mfc/docview.py @@ -0,0 +1,151 @@ +# document and view classes for MFC. +import win32ui + +from . import object, window + + +class View(window.Wnd): + def __init__(self, initobj): + window.Wnd.__init__(self, initobj) + + def OnInitialUpdate(self): + pass + + +# Simple control based views. +class CtrlView(View): + def __init__(self, doc, wndclass, style=0): + View.__init__(self, win32ui.CreateCtrlView(doc, wndclass, style)) + + +class EditView(CtrlView): + def __init__(self, doc): + View.__init__(self, win32ui.CreateEditView(doc)) + + +class RichEditView(CtrlView): + def __init__(self, doc): + View.__init__(self, win32ui.CreateRichEditView(doc)) + + +class ListView(CtrlView): + def __init__(self, doc): + View.__init__(self, win32ui.CreateListView(doc)) + + +class TreeView(CtrlView): + def __init__(self, doc): + View.__init__(self, win32ui.CreateTreeView(doc)) + + +# Other more advanced views. +class ScrollView(View): + def __init__(self, doc): + View.__init__(self, win32ui.CreateView(doc)) + + +class FormView(View): + def __init__(self, doc, id): + View.__init__(self, win32ui.CreateFormView(doc, id)) + + +class Document(object.CmdTarget): + def __init__(self, template, docobj=None): + if docobj is None: + docobj = template.DoCreateDoc() + object.CmdTarget.__init__(self, docobj) + + +class RichEditDoc(object.CmdTarget): + def __init__(self, template): + object.CmdTarget.__init__(self, template.DoCreateRichEditDoc()) + + +class CreateContext: + "A transient base class used as a CreateContext" + + def __init__(self, template, doc=None): + self.template = template + self.doc = doc + + def __del__(self): + self.close() + + def close(self): + self.doc = None + self.template = None + + +class DocTemplate(object.CmdTarget): + def __init__( + self, resourceId=None, MakeDocument=None, MakeFrame=None, MakeView=None + ): + if resourceId is None: + resourceId = win32ui.IDR_PYTHONTYPE + object.CmdTarget.__init__(self, self._CreateDocTemplate(resourceId)) + self.MakeDocument = MakeDocument + self.MakeFrame = MakeFrame + self.MakeView = MakeView + self._SetupSharedMenu_() + + def _SetupSharedMenu_(self): + pass # to be overridden by each "app" + + def _CreateDocTemplate(self, resourceId): + return win32ui.CreateDocTemplate(resourceId) + + def __del__(self): + object.CmdTarget.__del__(self) + + def CreateCreateContext(self, doc=None): + return CreateContext(self, doc) + + def CreateNewFrame(self, doc): + makeFrame = self.MakeFrame + if makeFrame is None: + makeFrame = window.MDIChildWnd + wnd = makeFrame() + context = self.CreateCreateContext(doc) + wnd.LoadFrame( + self.GetResourceID(), -1, None, context + ) # triggers OnCreateClient... + return wnd + + def CreateNewDocument(self): + makeDocument = self.MakeDocument + if makeDocument is None: + makeDocument = Document + return makeDocument(self) + + def CreateView(self, frame, context): + makeView = self.MakeView + if makeView is None: + makeView = EditView + view = makeView(context.doc) + view.CreateWindow(frame) + + +class RichEditDocTemplate(DocTemplate): + def __init__( + self, resourceId=None, MakeDocument=None, MakeFrame=None, MakeView=None + ): + if MakeView is None: + MakeView = RichEditView + if MakeDocument is None: + MakeDocument = RichEditDoc + DocTemplate.__init__(self, resourceId, MakeDocument, MakeFrame, MakeView) + + def _CreateDocTemplate(self, resourceId): + return win32ui.CreateRichEditDocTemplate(resourceId) + + +def t(): + class FormTemplate(DocTemplate): + def CreateView(self, frame, context): + makeView = self.MakeView + # view = FormView(context.doc, win32ui.IDD_PROPDEMO1) + view = ListView(context.doc) + view.CreateWindow(frame) + + t = FormTemplate() + return t.OpenDocumentFile(None) diff --git a/myenv/Lib/site-packages/pythonwin/pywin/mfc/object.py b/myenv/Lib/site-packages/pythonwin/pywin/mfc/object.py new file mode 100644 index 000000000..70138b6ee --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/mfc/object.py @@ -0,0 +1,66 @@ +# MFC base classes. + +import win32ui + + +class Object: + def __init__(self, initObj=None): + self.__dict__["_obj_"] = initObj + # self._obj_ = initObj + if initObj is not None: + initObj.AttachObject(self) + + def __del__(self): + self.close() + + def __getattr__( + self, attr + ): # Make this object look like the underlying win32ui one. + # During cleanup __dict__ is not available, causing recursive death. + if not attr.startswith("__"): + try: + o = self.__dict__["_obj_"] + if o is not None: + return getattr(o, attr) + # Only raise this error for non "internal" names - + # Python may be calling __len__, __nonzero__, etc, so + # we dont want this exception + if attr[0] != "_" and attr[-1] != "_": + raise win32ui.error("The MFC object has died.") + except KeyError: + # No _obj_ at all - dont report MFC object died when there isnt one! + pass + raise AttributeError(attr) + + def OnAttachedObjectDeath(self): + # print "object", self.__class__.__name__, "dieing" + self._obj_ = None + + def close(self): + if "_obj_" in self.__dict__: + if self._obj_ is not None: + self._obj_.AttachObject(None) + self._obj_ = None + + +class CmdTarget(Object): + def __init__(self, initObj): + Object.__init__(self, initObj) + + def HookNotifyRange(self, handler, firstID, lastID): + oldhandlers = [] + for i in range(firstID, lastID + 1): + oldhandlers.append(self.HookNotify(handler, i)) + return oldhandlers + + def HookCommandRange(self, handler, firstID, lastID): + oldhandlers = [] + for i in range(firstID, lastID + 1): + oldhandlers.append(self.HookCommand(handler, i)) + return oldhandlers + + def HookCommandUpdateRange(self, handler, firstID, lastID): + oldhandlers = [] + for i in range(firstID, lastID + 1): + oldhandlers.append(self.HookCommandUpdate(handler, i)) + return oldhandlers diff --git a/myenv/Lib/site-packages/pythonwin/pywin/mfc/thread.py b/myenv/Lib/site-packages/pythonwin/pywin/mfc/thread.py new file mode 100644 index 000000000..90a0a762c --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/mfc/thread.py @@ -0,0 +1,25 @@ +# Thread and application objects + +import win32ui + +from . import object + + +class WinThread(object.CmdTarget): + def __init__(self, initObj=None): + if initObj is None: + initObj = win32ui.CreateThread() + object.CmdTarget.__init__(self, initObj) + + def InitInstance(self): + pass # Default None/0 return indicates success for InitInstance() + + def ExitInstance(self): + pass + + +class WinApp(WinThread): + def __init__(self, initApp=None): + if initApp is None: + initApp = win32ui.GetApp() + WinThread.__init__(self, initApp) diff --git a/myenv/Lib/site-packages/pythonwin/pywin/mfc/window.py b/myenv/Lib/site-packages/pythonwin/pywin/mfc/window.py new file mode 100644 index 000000000..e52a7c1cf --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/mfc/window.py @@ -0,0 +1,50 @@ +# The MFCish window classes. +import win32con +import win32ui + +from . import object + + +class Wnd(object.CmdTarget): + def __init__(self, initobj=None): + object.CmdTarget.__init__(self, initobj) + if self._obj_: + self._obj_.HookMessage(self.OnDestroy, win32con.WM_DESTROY) + + def OnDestroy(self, msg): + pass + + +# NOTE NOTE - This facility is currently disabled in Pythonwin!!!!! +# Note - to process all messages for your window, add the following method +# to a derived class. This code provides default message handling (ie, is +# identical, except presumably in speed, as if the method did not exist at +# all, so presumably will be modified to test for specific messages to be +# useful! +# def WindowProc(self, msg, wParam, lParam): +# rc, lResult = self._obj_.OnWndMsg(msg, wParam, lParam) +# if not rc: lResult = self._obj_.DefWindowProc(msg, wParam, lParam) +# return lResult + + +class FrameWnd(Wnd): + def __init__(self, wnd): + Wnd.__init__(self, wnd) + + +class MDIChildWnd(FrameWnd): + def __init__(self, wnd=None): + if wnd is None: + wnd = win32ui.CreateMDIChild() + FrameWnd.__init__(self, wnd) + + def OnCreateClient(self, cp, context): + if context is not None and context.template is not None: + context.template.CreateView(self, context) + + +class MDIFrameWnd(FrameWnd): + def __init__(self, wnd=None): + if wnd is None: + wnd = win32ui.CreateMDIFrame() + FrameWnd.__init__(self, wnd) diff --git a/myenv/Lib/site-packages/pythonwin/pywin/scintilla/IDLEenvironment.py b/myenv/Lib/site-packages/pythonwin/pywin/scintilla/IDLEenvironment.py new file mode 100644 index 000000000..b1db50931 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/scintilla/IDLEenvironment.py @@ -0,0 +1,598 @@ +# Code that allows Pythonwin to pretend it is IDLE +# (at least as far as most IDLE extensions are concerned) + +import string +import sys + +import win32api +import win32con +import win32ui +from pywin import default_scintilla_encoding +from pywin.mfc.dialog import GetSimpleInput + +wordchars = string.ascii_uppercase + string.ascii_lowercase + string.digits + + +class TextError(Exception): # When a TclError would normally be raised. + pass + + +class EmptyRange(Exception): # Internally raised. + pass + + +def GetIDLEModule(module): + try: + # First get it from Pythonwin it is exists. + modname = "pywin.idle." + module + __import__(modname) + except ImportError as details: + msg = ( + "The IDLE extension '%s' can not be located.\r\n\r\n" + "Please correct the installation and restart the" + " application.\r\n\r\n%s" % (module, details) + ) + win32ui.MessageBox(msg) + return None + mod = sys.modules[modname] + mod.TclError = TextError # A hack that can go soon! + return mod + + +# A class that is injected into the IDLE auto-indent extension. +# It allows for decent performance when opening a new file, +# as auto-indent uses the tokenizer module to determine indents. +# The default AutoIndent readline method works OK, but it goes through +# this layer of Tk index indirection for every single line. For large files +# without indents (and even small files with indents :-) it was pretty slow! +def fast_readline(self): + if self.finished: + val = "" + else: + if "_scint_lines" not in self.__dict__: + # XXX - note - assumes this is only called once the file is loaded! + self._scint_lines = self.text.edit.GetTextRange().split("\n") + sl = self._scint_lines + i = self.i = self.i + 1 + if i >= len(sl): + val = "" + else: + val = sl[i] + "\n" + return val.encode(default_scintilla_encoding) + + +try: + GetIDLEModule("AutoIndent").IndentSearcher.readline = fast_readline +except AttributeError: # GetIDLEModule may return None + pass + + +# A class that attempts to emulate an IDLE editor window. +# Construct with a Pythonwin view. +class IDLEEditorWindow: + def __init__(self, edit): + self.edit = edit + self.text = TkText(edit) + self.extensions = {} + self.extension_menus = {} + + def close(self): + self.edit = self.text = None + self.extension_menus = None + try: + for ext in self.extensions.values(): + closer = getattr(ext, "close", None) + if closer is not None: + closer() + finally: + self.extensions = {} + + def IDLEExtension(self, extension): + ext = self.extensions.get(extension) + if ext is not None: + return ext + mod = GetIDLEModule(extension) + if mod is None: + return None + klass = getattr(mod, extension) + ext = self.extensions[extension] = klass(self) + # Find and bind all the events defined in the extension. + events = [item for item in dir(klass) if item[-6:] == "_event"] + for event in events: + name = "<<%s>>" % (event[:-6].replace("_", "-"),) + self.edit.bindings.bind(name, getattr(ext, event)) + return ext + + def GetMenuItems(self, menu_name): + # Get all menu items for the menu name (eg, "edit") + bindings = self.edit.bindings + ret = [] + for ext in self.extensions.values(): + menudefs = getattr(ext, "menudefs", []) + for name, items in menudefs: + if name == menu_name: + for text, event in [item for item in items if item is not None]: + text = text.replace("&", "&&") + text = text.replace("_", "&") + ret.append((text, event)) + return ret + + ###################################################################### + # The IDLE "Virtual UI" methods that are exposed to the IDLE extensions. + # + def askinteger( + self, caption, prompt, parent=None, initialvalue=0, minvalue=None, maxvalue=None + ): + while 1: + rc = GetSimpleInput(prompt, str(initialvalue), caption) + if rc is None: + return 0 # Correct "cancel" semantics? + err = None + try: + rc = int(rc) + except ValueError: + err = "Please enter an integer" + if not err and minvalue is not None and rc < minvalue: + err = "Please enter an integer greater then or equal to %s" % ( + minvalue, + ) + if not err and maxvalue is not None and rc > maxvalue: + err = "Please enter an integer less then or equal to %s" % (maxvalue,) + if err: + win32ui.MessageBox(err, caption, win32con.MB_OK) + continue + return rc + + def askyesno(self, caption, prompt, parent=None): + return win32ui.MessageBox(prompt, caption, win32con.MB_YESNO) == win32con.IDYES + + ###################################################################### + # The IDLE "Virtual Text Widget" methods that are exposed to the IDLE extensions. + # + + # Is character at text_index in a Python string? Return 0 for + # "guaranteed no", true for anything else. + def is_char_in_string(self, text_index): + # A helper for the code analyser - we need internal knowledge of + # the colorizer to get this information + # This assumes the colorizer has got to this point! + text_index = self.text._getoffset(text_index) + c = self.text.edit._GetColorizer() + if c and c.GetStringStyle(text_index) is None: + return 0 + return 1 + + # If a selection is defined in the text widget, return + # (start, end) as Tkinter text indices, otherwise return + # (None, None) + def get_selection_indices(self): + try: + first = self.text.index("sel.first") + last = self.text.index("sel.last") + return first, last + except TextError: + return None, None + + def set_tabwidth(self, width): + self.edit.SCISetTabWidth(width) + + def get_tabwidth(self): + return self.edit.GetTabWidth() + + +# A class providing the generic "Call Tips" interface +class CallTips: + def __init__(self, edit): + self.edit = edit + + def showtip(self, tip_text): + self.edit.SCICallTipShow(tip_text) + + def hidetip(self): + self.edit.SCICallTipCancel() + + +######################################## +# +# Helpers for the TkText emulation. +def TkOffsetToIndex(offset, edit): + lineoff = 0 + # May be 1 > actual end if we pretended there was a trailing '\n' + offset = min(offset, edit.GetTextLength()) + line = edit.LineFromChar(offset) + lineIndex = edit.LineIndex(line) + return "%d.%d" % (line + 1, offset - lineIndex) + + +def _NextTok(str, pos): + # Returns (token, endPos) + end = len(str) + if pos >= end: + return None, 0 + while pos < end and str[pos] in string.whitespace: + pos = pos + 1 + # Special case for +- + if str[pos] in "+-": + return str[pos], pos + 1 + # Digits also a special case. + endPos = pos + while endPos < end and str[endPos] in string.digits + ".": + endPos = endPos + 1 + if pos != endPos: + return str[pos:endPos], endPos + endPos = pos + while endPos < end and str[endPos] not in string.whitespace + string.digits + "+-": + endPos = endPos + 1 + if pos != endPos: + return str[pos:endPos], endPos + return None, 0 + + +def TkIndexToOffset(bm, edit, marks): + base, nextTokPos = _NextTok(bm, 0) + if base is None: + raise ValueError("Empty bookmark ID!") + if base.find(".") > 0: + try: + line, col = base.split(".", 2) + if col == "first" or col == "last": + # Tag name + if line != "sel": + raise ValueError("Tags arent here!") + sel = edit.GetSel() + if sel[0] == sel[1]: + raise EmptyRange + if col == "first": + pos = sel[0] + else: + pos = sel[1] + else: + # Lines are 1 based for tkinter + line = int(line) - 1 + if line > edit.GetLineCount(): + pos = edit.GetTextLength() + 1 + else: + pos = edit.LineIndex(line) + if pos == -1: + pos = edit.GetTextLength() + pos = pos + int(col) + except (ValueError, IndexError): + raise ValueError("Unexpected literal in '%s'" % base) + elif base == "insert": + pos = edit.GetSel()[0] + elif base == "end": + pos = edit.GetTextLength() + # Pretend there is a trailing '\n' if necessary + if pos and edit.SCIGetCharAt(pos - 1) != "\n": + pos = pos + 1 + else: + try: + pos = marks[base] + except KeyError: + raise ValueError("Unsupported base offset or undefined mark '%s'" % base) + + while 1: + word, nextTokPos = _NextTok(bm, nextTokPos) + if word is None: + break + if word in ("+", "-"): + num, nextTokPos = _NextTok(bm, nextTokPos) + if num is None: + raise ValueError("+/- operator needs 2 args") + what, nextTokPos = _NextTok(bm, nextTokPos) + if what is None: + raise ValueError("+/- operator needs 2 args") + if what[0] != "c": + raise ValueError("+/- only supports chars") + if word == "+": + pos = pos + int(num) + else: + pos = pos - int(num) + elif word == "wordstart": + while pos > 0 and edit.SCIGetCharAt(pos - 1) in wordchars: + pos = pos - 1 + elif word == "wordend": + end = edit.GetTextLength() + while pos < end and edit.SCIGetCharAt(pos) in wordchars: + pos = pos + 1 + elif word == "linestart": + while pos > 0 and edit.SCIGetCharAt(pos - 1) not in "\n\r": + pos = pos - 1 + elif word == "lineend": + end = edit.GetTextLength() + while pos < end and edit.SCIGetCharAt(pos) not in "\n\r": + pos = pos + 1 + else: + raise ValueError("Unsupported relative offset '%s'" % word) + return max(pos, 0) # Tkinter is tollerant of -ve indexes - we aren't + + +# A class that resembles an IDLE (ie, a Tk) text widget. +# Construct with an edit object (eg, an editor view) +class TkText: + def __init__(self, edit): + self.calltips = None + self.edit = edit + self.marks = {} + + ## def __getattr__(self, attr): + ## if attr=="tk": return self # So text.tk.call works. + ## if attr=="master": return None # ditto! + ## raise AttributeError, attr + ## def __getitem__(self, item): + ## if item=="tabs": + ## size = self.edit.GetTabWidth() + ## if size==8: return "" # Tk default + ## return size # correct semantics? + ## elif item=="font": # Used for measurements we dont need to do! + ## return "Dont know the font" + ## raise IndexError, "Invalid index '%s'" % item + def make_calltip_window(self): + if self.calltips is None: + self.calltips = CallTips(self.edit) + return self.calltips + + def _getoffset(self, index): + return TkIndexToOffset(index, self.edit, self.marks) + + def _getindex(self, off): + return TkOffsetToIndex(off, self.edit) + + def _fix_indexes(self, start, end): + # first some magic to handle skipping over utf8 extended chars. + while start > 0 and ord(self.edit.SCIGetCharAt(start)) & 0xC0 == 0x80: + start -= 1 + while ( + end < self.edit.GetTextLength() + and ord(self.edit.SCIGetCharAt(end)) & 0xC0 == 0x80 + ): + end += 1 + # now handling fixing \r\n->\n disparities... + if ( + start > 0 + and self.edit.SCIGetCharAt(start) == "\n" + and self.edit.SCIGetCharAt(start - 1) == "\r" + ): + start = start - 1 + if ( + end < self.edit.GetTextLength() + and self.edit.SCIGetCharAt(end - 1) == "\r" + and self.edit.SCIGetCharAt(end) == "\n" + ): + end = end + 1 + return start, end + + ## def get_tab_width(self): + ## return self.edit.GetTabWidth() + ## def call(self, *rest): + ## # Crap to support Tk measurement hacks for tab widths + ## if rest[0] != "font" or rest[1] != "measure": + ## raise ValueError, "Unsupport call type" + ## return len(rest[5]) + ## def configure(self, **kw): + ## for name, val in kw.items(): + ## if name=="tabs": + ## self.edit.SCISetTabWidth(int(val)) + ## else: + ## raise ValueError, "Unsupported configuration item %s" % kw + def bind(self, binding, handler): + self.edit.bindings.bind(binding, handler) + + def get(self, start, end=None): + try: + start = self._getoffset(start) + if end is None: + end = start + 1 + else: + end = self._getoffset(end) + except EmptyRange: + return "" + # Simple semantic checks to conform to the Tk text interface + if end <= start: + return "" + max = self.edit.GetTextLength() + checkEnd = 0 + if end > max: + end = max + checkEnd = 1 + start, end = self._fix_indexes(start, end) + ret = self.edit.GetTextRange(start, end) + # pretend a trailing '\n' exists if necessary. + if checkEnd and (not ret or ret[-1] != "\n"): + ret = ret + "\n" + return ret.replace("\r", "") + + def index(self, spec): + try: + return self._getindex(self._getoffset(spec)) + except EmptyRange: + return "" + + def insert(self, pos, text): + try: + pos = self._getoffset(pos) + except EmptyRange: + raise TextError("Empty range") + self.edit.SetSel((pos, pos)) + # IDLE only deals with "\n" - we will be nicer + + bits = text.split("\n") + self.edit.SCIAddText(bits[0]) + for bit in bits[1:]: + self.edit.SCINewline() + self.edit.SCIAddText(bit) + + def delete(self, start, end=None): + try: + start = self._getoffset(start) + if end is not None: + end = self._getoffset(end) + except EmptyRange: + raise TextError("Empty range") + # If end is specified and == start, then we must delete nothing. + if start == end: + return + # If end is not specified, delete one char + if end is None: + end = start + 1 + else: + # Tk says not to delete in this case, but our control would. + if end < start: + return + if start == self.edit.GetTextLength(): + return # Nothing to delete. + old = self.edit.GetSel()[0] # Lose a selection + # Hack for partial '\r\n' and UTF-8 char removal + start, end = self._fix_indexes(start, end) + self.edit.SetSel((start, end)) + self.edit.Clear() + if old >= start and old < end: + old = start + elif old >= end: + old = old - (end - start) + self.edit.SetSel(old) + + def bell(self): + win32api.MessageBeep() + + def see(self, pos): + # Most commands we use in Scintilla actually force the selection + # to be seen, making this unnecessary. + pass + + def mark_set(self, name, pos): + try: + pos = self._getoffset(pos) + except EmptyRange: + raise TextError("Empty range '%s'" % pos) + if name == "insert": + self.edit.SetSel(pos) + else: + self.marks[name] = pos + + def tag_add(self, name, start, end): + if name != "sel": + raise ValueError("Only sel tag is supported") + try: + start = self._getoffset(start) + end = self._getoffset(end) + except EmptyRange: + raise TextError("Empty range") + self.edit.SetSel(start, end) + + def tag_remove(self, name, start, end): + if name != "sel" or start != "1.0" or end != "end": + raise ValueError("Cant remove this tag") + # Turn the sel into a cursor + self.edit.SetSel(self.edit.GetSel()[0]) + + def compare(self, i1, op, i2): + try: + i1 = self._getoffset(i1) + except EmptyRange: + i1 = "" + try: + i2 = self._getoffset(i2) + except EmptyRange: + i2 = "" + return eval("%d%s%d" % (i1, op, i2)) + + def undo_block_start(self): + self.edit.SCIBeginUndoAction() + + def undo_block_stop(self): + self.edit.SCIEndUndoAction() + + +###################################################################### +# +# Test related code. +# +###################################################################### +def TestCheck(index, edit, expected=None): + rc = TkIndexToOffset(index, edit, {}) + if rc != expected: + print("ERROR: Index", index, ", expected", expected, "but got", rc) + + +def TestGet(fr, to, t, expected): + got = t.get(fr, to) + if got != expected: + print( + "ERROR: get(%s, %s) expected %s, but got %s" + % (repr(fr), repr(to), repr(expected), repr(got)) + ) + + +def test(): + import pywin.framework.editor + + d = pywin.framework.editor.editorTemplate.OpenDocumentFile(None) + e = d.GetFirstView() + t = TkText(e) + e.SCIAddText("hi there how\nare you today\r\nI hope you are well") + e.SetSel((4, 4)) + + skip = """ + TestCheck("insert", e, 4) + TestCheck("insert wordstart", e, 3) + TestCheck("insert wordend", e, 8) + TestCheck("insert linestart", e, 0) + TestCheck("insert lineend", e, 12) + TestCheck("insert + 4 chars", e, 8) + TestCheck("insert +4c", e, 8) + TestCheck("insert - 2 chars", e, 2) + TestCheck("insert -2c", e, 2) + TestCheck("insert-2c", e, 2) + TestCheck("insert-2 c", e, 2) + TestCheck("insert- 2c", e, 2) + TestCheck("1.1", e, 1) + TestCheck("1.0", e, 0) + TestCheck("2.0", e, 13) + try: + TestCheck("sel.first", e, 0) + print "*** sel.first worked with an empty selection" + except TextError: + pass + e.SetSel((4,5)) + TestCheck("sel.first- 2c", e, 2) + TestCheck("sel.last- 2c", e, 3) + """ + # Check EOL semantics + e.SetSel((4, 4)) + TestGet("insert lineend", "insert lineend +1c", t, "\n") + e.SetSel((20, 20)) + TestGet("insert lineend", "insert lineend +1c", t, "\n") + e.SetSel((35, 35)) + TestGet("insert lineend", "insert lineend +1c", t, "\n") + + +class IDLEWrapper: + def __init__(self, control): + self.text = control + + +def IDLETest(extension): + import os + import sys + + modname = "pywin.idle." + extension + __import__(modname) + mod = sys.modules[modname] + mod.TclError = TextError + klass = getattr(mod, extension) + + # Create a new Scintilla Window. + import pywin.framework.editor + + d = pywin.framework.editor.editorTemplate.OpenDocumentFile(None) + v = d.GetFirstView() + fname = os.path.splitext(__file__)[0] + ".py" + v.SCIAddText(open(fname).read()) + d.SetModifiedFlag(0) + r = klass(IDLEWrapper(TkText(v))) + return r + + +if __name__ == "__main__": + test() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/scintilla/__init__.py b/myenv/Lib/site-packages/pythonwin/pywin/scintilla/__init__.py new file mode 100644 index 000000000..0981b6228 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/scintilla/__init__.py @@ -0,0 +1 @@ +# package init. diff --git a/myenv/Lib/site-packages/pythonwin/pywin/scintilla/bindings.py b/myenv/Lib/site-packages/pythonwin/pywin/scintilla/bindings.py new file mode 100644 index 000000000..785bd42a1 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/scintilla/bindings.py @@ -0,0 +1,178 @@ +import traceback + +import win32api +import win32con +import win32ui + +from . import IDLEenvironment, keycodes + +HANDLER_ARGS_GUESS = 0 +HANDLER_ARGS_NATIVE = 1 +HANDLER_ARGS_IDLE = 2 +HANDLER_ARGS_EXTENSION = 3 + +next_id = 5000 + +event_to_commands = {} # dict of integer IDs to event names. +command_to_events = {} # dict of event names to int IDs + + +def assign_command_id(event, id=0): + global next_id + if id == 0: + id = event_to_commands.get(event, 0) + if id == 0: + id = next_id + next_id = next_id + 1 + # Only map the ones we allocated - specified ones are assumed to have a handler + command_to_events[id] = event + event_to_commands[event] = id + return id + + +class SendCommandHandler: + def __init__(self, cmd): + self.cmd = cmd + + def __call__(self, *args): + win32ui.GetMainFrame().SendMessage(win32con.WM_COMMAND, self.cmd) + + +class Binding: + def __init__(self, handler, handler_args_type): + self.handler = handler + self.handler_args_type = handler_args_type + + +class BindingsManager: + def __init__(self, parent_view): + self.parent_view = parent_view + self.bindings = {} # dict of Binding instances. + self.keymap = {} + + def prepare_configure(self): + self.keymap = {} + + def complete_configure(self): + for id in command_to_events.keys(): + self.parent_view.HookCommand(self._OnCommand, id) + + def close(self): + self.parent_view = self.bindings = self.keymap = None + + def report_error(self, problem): + try: + win32ui.SetStatusText(problem, 1) + except win32ui.error: + # No status bar! + print(problem) + + def update_keymap(self, keymap): + self.keymap.update(keymap) + + def bind(self, event, handler, handler_args_type=HANDLER_ARGS_GUESS, cid=0): + if handler is None: + handler = SendCommandHandler(cid) + self.bindings[event] = self._new_binding(handler, handler_args_type) + self.bind_command(event, cid) + + def bind_command(self, event, id=0): + "Binds an event to a Windows control/command ID" + id = assign_command_id(event, id) + return id + + def get_command_id(self, event): + id = event_to_commands.get(event) + if id is None: + # See if we even have an event of that name!? + if event not in self.bindings: + return None + id = self.bind_command(event) + return id + + def _OnCommand(self, id, code): + event = command_to_events.get(id) + if event is None: + self.report_error("No event associated with event ID %d" % id) + return 1 + return self.fire(event) + + def _new_binding(self, event, handler_args_type): + return Binding(event, handler_args_type) + + def _get_IDLE_handler(self, ext, handler): + try: + instance = self.parent_view.idle.IDLEExtension(ext) + name = handler.replace("-", "_") + "_event" + return getattr(instance, name) + except (ImportError, AttributeError): + msg = "Can not find event '%s' in IDLE extension '%s'" % (handler, ext) + self.report_error(msg) + return None + + def fire(self, event, event_param=None): + # Fire the specified event. Result is native Pythonwin result + # (ie, 1==pass one, 0 or None==handled) + + # First look up the event directly - if there, we are set. + binding = self.bindings.get(event) + if binding is None: + # If possible, find it! + # A native method name + handler = getattr(self.parent_view, event + "Event", None) + if handler is None: + # Can't decide if I should report an error?? + self.report_error("The event name '%s' can not be found." % event) + # Either way, just let the default handlers grab it. + return 1 + binding = self._new_binding(handler, HANDLER_ARGS_NATIVE) + # Cache it. + self.bindings[event] = binding + + handler_args_type = binding.handler_args_type + # Now actually fire it. + if handler_args_type == HANDLER_ARGS_GUESS: + # Can't be native, as natives are never added with "guess". + # Must be extension or IDLE. + if event[0] == "<": + handler_args_type = HANDLER_ARGS_IDLE + else: + handler_args_type = HANDLER_ARGS_EXTENSION + try: + if handler_args_type == HANDLER_ARGS_EXTENSION: + args = self.parent_view.idle, event_param + else: + args = (event_param,) + rc = binding.handler(*args) + if handler_args_type == HANDLER_ARGS_IDLE: + # Convert to our return code. + if rc in (None, "break"): + rc = 0 + else: + rc = 1 + except: + message = "Firing event '%s' failed." % event + print(message) + traceback.print_exc() + self.report_error(message) + rc = 1 # Let any default handlers have a go! + return rc + + def fire_key_event(self, msg): + key = msg[2] + keyState = 0 + if win32api.GetKeyState(win32con.VK_CONTROL) & 0x8000: + keyState = ( + keyState | win32con.RIGHT_CTRL_PRESSED | win32con.LEFT_CTRL_PRESSED + ) + if win32api.GetKeyState(win32con.VK_SHIFT) & 0x8000: + keyState = keyState | win32con.SHIFT_PRESSED + if win32api.GetKeyState(win32con.VK_MENU) & 0x8000: + keyState = keyState | win32con.LEFT_ALT_PRESSED | win32con.RIGHT_ALT_PRESSED + keyinfo = key, keyState + # Special hacks for the dead-char key on non-US keyboards. + # (XXX - which do not work :-( + event = self.keymap.get(keyinfo) + if event is None: + return 1 + return self.fire(event, None) diff --git a/myenv/Lib/site-packages/pythonwin/pywin/scintilla/config.py b/myenv/Lib/site-packages/pythonwin/pywin/scintilla/config.py new file mode 100644 index 000000000..9a1b210e9 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/scintilla/config.py @@ -0,0 +1,365 @@ +# config.py - deals with loading configuration information. + +# Loads config data from a .cfg file. Also caches the compiled +# data back into a .cfc file. + +# If you are wondering how to avoid needing .cfg files (eg, +# if you are freezing Pythonwin etc) I suggest you create a +# .py file, and put the config info in a docstring. Then +# pass a CStringIO file (rather than a filename) to the +# config manager. +import glob +import importlib.util +import marshal +import os +import stat +import sys +import traceback +import types + +import pywin +import win32api + +from . import keycodes + +debugging = 0 +if debugging: + import win32traceutil # Some trace statements fire before the interactive window is open. + + def trace(*args): + sys.stderr.write(" ".join(map(str, args)) + "\n") + +else: + trace = lambda *args: None + +compiled_config_version = 3 + + +def split_line(line, lineno): + comment_pos = line.find("#") + if comment_pos >= 0: + line = line[:comment_pos] + sep_pos = line.rfind("=") + if sep_pos == -1: + if line.strip(): + print("Warning: Line %d: %s is an invalid entry" % (lineno, repr(line))) + return None, None + return "", "" + return line[:sep_pos].strip(), line[sep_pos + 1 :].strip() + + +def get_section_header(line): + # Returns the section if the line is a section header, else None + if line[0] == "[": + end = line.find("]") + if end == -1: + end = len(line) + rc = line[1:end].lower() + try: + i = rc.index(":") + return rc[:i], rc[i + 1 :] + except ValueError: + return rc, "" + return None, None + + +def find_config_file(f): + return os.path.join(pywin.__path__[0], f + ".cfg") + + +def find_config_files(): + return [ + os.path.split(x)[1] + for x in [ + os.path.splitext(x)[0] + for x in glob.glob(os.path.join(pywin.__path__[0], "*.cfg")) + ] + ] + + +class ConfigManager: + def __init__(self, f): + self.filename = "unknown" + self.last_error = None + self.key_to_events = {} + b_close = False + if hasattr(f, "readline"): + fp = f + self.filename = "" + compiled_name = None + else: + try: + f = find_config_file(f) + src_stat = os.stat(f) + except os.error: + self.report_error("Config file '%s' not found" % f) + return + self.filename = f + self.basename = os.path.basename(f) + trace("Loading configuration", self.basename) + compiled_name = os.path.splitext(f)[0] + ".cfc" + try: + cf = open(compiled_name, "rb") + try: + ver = marshal.load(cf) + ok = compiled_config_version == ver + if ok: + kblayoutname = marshal.load(cf) + magic = marshal.load(cf) + size = marshal.load(cf) + mtime = marshal.load(cf) + if ( + magic == importlib.util.MAGIC_NUMBER + and win32api.GetKeyboardLayoutName() == kblayoutname + and src_stat[stat.ST_MTIME] == mtime + and src_stat[stat.ST_SIZE] == size + ): + self.cache = marshal.load(cf) + trace("Configuration loaded cached", compiled_name) + return # We are ready to roll! + finally: + cf.close() + except (os.error, IOError, EOFError): + pass + fp = open(f) + b_close = True + self.cache = {} + lineno = 1 + line = fp.readline() + while line: + # Skip to the next section (maybe already there!) + section, subsection = get_section_header(line) + while line and section is None: + line = fp.readline() + if not line: + break + lineno = lineno + 1 + section, subsection = get_section_header(line) + if not line: + break + + if section == "keys": + line, lineno = self._load_keys(subsection, fp, lineno) + elif section == "extensions": + line, lineno = self._load_extensions(subsection, fp, lineno) + elif section == "idle extensions": + line, lineno = self._load_idle_extensions(subsection, fp, lineno) + elif section == "general": + line, lineno = self._load_general(subsection, fp, lineno) + else: + self.report_error( + "Unrecognised section header '%s:%s'" % (section, subsection) + ) + line = fp.readline() + lineno = lineno + 1 + if b_close: + fp.close() + # Check critical data. + if not self.cache.get("keys"): + self.report_error("No keyboard definitions were loaded") + if not self.last_error and compiled_name: + try: + cf = open(compiled_name, "wb") + marshal.dump(compiled_config_version, cf) + marshal.dump(win32api.GetKeyboardLayoutName(), cf) + marshal.dump(importlib.util.MAGIC_NUMBER, cf) + marshal.dump(src_stat[stat.ST_SIZE], cf) + marshal.dump(src_stat[stat.ST_MTIME], cf) + marshal.dump(self.cache, cf) + cf.close() + except (IOError, EOFError): + pass # Ignore errors - may be read only. + + def configure(self, editor, subsections=None): + # Execute the extension code, and find any events. + # First, we "recursively" connect any we are based on. + if subsections is None: + subsections = [] + subsections = [""] + subsections + general = self.get_data("general") + if general: + parents = general.get("based on", []) + for parent in parents: + trace("Configuration based on", parent, "- loading.") + parent = self.__class__(parent) + parent.configure(editor, subsections) + if parent.last_error: + self.report_error(parent.last_error) + + bindings = editor.bindings + codeob = self.get_data("extension code") + if codeob is not None: + ns = {} + try: + exec(codeob, ns) + except: + traceback.print_exc() + self.report_error("Executing extension code failed") + ns = None + if ns: + num = 0 + for name, func in list(ns.items()): + if type(func) == types.FunctionType and name[:1] != "_": + bindings.bind(name, func) + num = num + 1 + trace("Configuration Extension code loaded", num, "events") + # Load the idle extensions + for subsection in subsections: + for ext in self.get_data("idle extensions", {}).get(subsection, []): + try: + editor.idle.IDLEExtension(ext) + trace("Loaded IDLE extension", ext) + except: + self.report_error("Can not load the IDLE extension '%s'" % ext) + + # Now bind up the key-map (remembering a reverse map + subsection_keymap = self.get_data("keys") + num_bound = 0 + for subsection in subsections: + keymap = subsection_keymap.get(subsection, {}) + bindings.update_keymap(keymap) + num_bound = num_bound + len(keymap) + trace("Configuration bound", num_bound, "keys") + + def get_key_binding(self, event, subsections=None): + if subsections is None: + subsections = [] + subsections = [""] + subsections + + subsection_keymap = self.get_data("keys") + for subsection in subsections: + map = self.key_to_events.get(subsection) + if map is None: # Build it + map = {} + keymap = subsection_keymap.get(subsection, {}) + for key_info, map_event in list(keymap.items()): + map[map_event] = key_info + self.key_to_events[subsection] = map + + info = map.get(event) + if info is not None: + return keycodes.make_key_name(info[0], info[1]) + return None + + def report_error(self, msg): + self.last_error = msg + print("Error in %s: %s" % (self.filename, msg)) + + def report_warning(self, msg): + print("Warning in %s: %s" % (self.filename, msg)) + + def _readline(self, fp, lineno, bStripComments=1): + line = fp.readline() + lineno = lineno + 1 + if line: + bBreak = ( + get_section_header(line)[0] is not None + ) # A new section is starting + if bStripComments and not bBreak: + pos = line.find("#") + if pos >= 0: + line = line[:pos] + "\n" + else: + bBreak = 1 + return line, lineno, bBreak + + def get_data(self, name, default=None): + return self.cache.get(name, default) + + def _save_data(self, name, data): + self.cache[name] = data + return data + + def _load_general(self, sub_section, fp, lineno): + map = {} + while 1: + line, lineno, bBreak = self._readline(fp, lineno) + if bBreak: + break + + key, val = split_line(line, lineno) + if not key: + continue + key = key.lower() + l = map.get(key, []) + l.append(val) + map[key] = l + self._save_data("general", map) + return line, lineno + + def _load_keys(self, sub_section, fp, lineno): + # Builds a nested dictionary of + # (scancode, flags) = event_name + main_map = self.get_data("keys", {}) + map = main_map.get(sub_section, {}) + while 1: + line, lineno, bBreak = self._readline(fp, lineno) + if bBreak: + break + + key, event = split_line(line, lineno) + if not event: + continue + sc, flag = keycodes.parse_key_name(key) + if sc is None: + self.report_warning("Line %d: Invalid key name '%s'" % (lineno, key)) + else: + map[sc, flag] = event + main_map[sub_section] = map + self._save_data("keys", main_map) + return line, lineno + + def _load_extensions(self, sub_section, fp, lineno): + start_lineno = lineno + lines = [] + while 1: + line, lineno, bBreak = self._readline(fp, lineno, 0) + if bBreak: + break + lines.append(line) + try: + c = compile( + "\n" * start_lineno + "".join(lines), # produces correct tracebacks + self.filename, + "exec", + ) + self._save_data("extension code", c) + except SyntaxError as details: + errlineno = details.lineno + start_lineno + # Should handle syntax errors better here, and offset the lineno. + self.report_error( + "Compiling extension code failed:\r\nFile: %s\r\nLine %d\r\n%s" + % (details.filename, errlineno, details.msg) + ) + return line, lineno + + def _load_idle_extensions(self, sub_section, fp, lineno): + extension_map = self.get_data("idle extensions") + if extension_map is None: + extension_map = {} + extensions = [] + while 1: + line, lineno, bBreak = self._readline(fp, lineno) + if bBreak: + break + line = line.strip() + if line: + extensions.append(line) + extension_map[sub_section] = extensions + self._save_data("idle extensions", extension_map) + return line, lineno + + +def test(): + import time + + start = time.clock() + f = "default" + cm = ConfigManager(f) + map = cm.get_data("keys") + took = time.clock() - start + print("Loaded %s items in %.4f secs" % (len(map), took)) + + +if __name__ == "__main__": + test() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/scintilla/configui.py b/myenv/Lib/site-packages/pythonwin/pywin/scintilla/configui.py new file mode 100644 index 000000000..8b6157b60 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/scintilla/configui.py @@ -0,0 +1,292 @@ +import win32api +import win32con +import win32ui +from pywin.mfc import dialog + +# Used to indicate that style should use default color +from win32con import CLR_INVALID + +from . import scintillacon + +###################################################### +# Property Page for syntax formatting options + +# The standard 16 color VGA palette should always be possible +paletteVGA = ( + ("Black", win32api.RGB(0, 0, 0)), + ("Navy", win32api.RGB(0, 0, 128)), + ("Green", win32api.RGB(0, 128, 0)), + ("Cyan", win32api.RGB(0, 128, 128)), + ("Maroon", win32api.RGB(128, 0, 0)), + ("Purple", win32api.RGB(128, 0, 128)), + ("Olive", win32api.RGB(128, 128, 0)), + ("Gray", win32api.RGB(128, 128, 128)), + ("Silver", win32api.RGB(192, 192, 192)), + ("Blue", win32api.RGB(0, 0, 255)), + ("Lime", win32api.RGB(0, 255, 0)), + ("Aqua", win32api.RGB(0, 255, 255)), + ("Red", win32api.RGB(255, 0, 0)), + ("Fuchsia", win32api.RGB(255, 0, 255)), + ("Yellow", win32api.RGB(255, 255, 0)), + ("White", win32api.RGB(255, 255, 255)), + # and a few others will generally be possible. + ("DarkGrey", win32api.RGB(64, 64, 64)), + ("PurpleBlue", win32api.RGB(64, 64, 192)), + ("DarkGreen", win32api.RGB(0, 96, 0)), + ("DarkOlive", win32api.RGB(128, 128, 64)), + ("MediumBlue", win32api.RGB(0, 0, 192)), + ("DarkNavy", win32api.RGB(0, 0, 96)), + ("Magenta", win32api.RGB(96, 0, 96)), + ("OffWhite", win32api.RGB(255, 255, 220)), + ("LightPurple", win32api.RGB(220, 220, 255)), + ("", win32con.CLR_INVALID), +) + + +class ScintillaFormatPropertyPage(dialog.PropertyPage): + def __init__(self, scintillaClass=None, caption=0): + self.scintillaClass = scintillaClass + dialog.PropertyPage.__init__(self, win32ui.IDD_PP_FORMAT, caption=caption) + + def OnInitDialog(self): + try: + if self.scintillaClass is None: + from . import control + + sc = control.CScintillaEdit + else: + sc = self.scintillaClass + + self.scintilla = sc() + style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.ES_MULTILINE + # Convert the rect size + rect = self.MapDialogRect((5, 5, 120, 75)) + self.scintilla.CreateWindow(style, rect, self, 111) + self.HookNotify(self.OnBraceMatch, scintillacon.SCN_CHECKBRACE) + self.scintilla.HookKeyStroke(self.OnEsc, 27) + self.scintilla.SCISetViewWS(1) + self.pos_bstart = self.pos_bend = self.pos_bbad = 0 + + colorizer = self.scintilla._GetColorizer() + text = colorizer.GetSampleText() + items = text.split("|", 2) + pos = len(items[0]) + self.scintilla.SCIAddText("".join(items)) + self.scintilla.SetSel(pos, pos) + self.scintilla.ApplyFormattingStyles() + self.styles = self.scintilla._GetColorizer().styles + + self.cbo = self.GetDlgItem(win32ui.IDC_COMBO1) + for c in paletteVGA: + self.cbo.AddString(c[0]) + + self.cboBoldItalic = self.GetDlgItem(win32ui.IDC_COMBO2) + for item in ("Bold Italic", "Bold", "Italic", "Regular"): + self.cboBoldItalic.InsertString(0, item) + + self.butIsDefault = self.GetDlgItem(win32ui.IDC_CHECK1) + self.butIsDefaultBackground = self.GetDlgItem(win32ui.IDC_CHECK2) + self.listbox = self.GetDlgItem(win32ui.IDC_LIST1) + self.HookCommand(self.OnListCommand, win32ui.IDC_LIST1) + names = list(self.styles.keys()) + names.sort() + for name in names: + if self.styles[name].aliased is None: + self.listbox.AddString(name) + self.listbox.SetCurSel(0) + + idc = win32ui.IDC_RADIO1 + if not self.scintilla._GetColorizer().bUseFixed: + idc = win32ui.IDC_RADIO2 + self.GetDlgItem(idc).SetCheck(1) + self.UpdateUIForStyle(self.styles[names[0]]) + + self.scintilla.HookFormatter(self) + self.HookCommand(self.OnButDefaultFixedFont, win32ui.IDC_BUTTON1) + self.HookCommand(self.OnButDefaultPropFont, win32ui.IDC_BUTTON2) + self.HookCommand(self.OnButThisFont, win32ui.IDC_BUTTON3) + self.HookCommand(self.OnButUseDefaultFont, win32ui.IDC_CHECK1) + self.HookCommand(self.OnButThisBackground, win32ui.IDC_BUTTON4) + self.HookCommand(self.OnButUseDefaultBackground, win32ui.IDC_CHECK2) + self.HookCommand(self.OnStyleUIChanged, win32ui.IDC_COMBO1) + self.HookCommand(self.OnStyleUIChanged, win32ui.IDC_COMBO2) + self.HookCommand(self.OnButFixedOrDefault, win32ui.IDC_RADIO1) + self.HookCommand(self.OnButFixedOrDefault, win32ui.IDC_RADIO2) + except: + import traceback + + traceback.print_exc() + + def OnEsc(self, ch): + self.GetParent().EndDialog(win32con.IDCANCEL) + + def OnBraceMatch(self, std, extra): + import pywin.scintilla.view + + pywin.scintilla.view.DoBraceMatch(self.scintilla) + + def GetSelectedStyle(self): + return self.styles[self.listbox.GetText(self.listbox.GetCurSel())] + + def _DoButDefaultFont(self, extra_flags, attr): + baseFormat = getattr(self.scintilla._GetColorizer(), attr) + flags = ( + extra_flags + | win32con.CF_SCREENFONTS + | win32con.CF_EFFECTS + | win32con.CF_FORCEFONTEXIST + ) + d = win32ui.CreateFontDialog(baseFormat, flags, None, self) + if d.DoModal() == win32con.IDOK: + setattr(self.scintilla._GetColorizer(), attr, d.GetCharFormat()) + self.OnStyleUIChanged(0, win32con.BN_CLICKED) + + def OnButDefaultFixedFont(self, id, code): + if code == win32con.BN_CLICKED: + self._DoButDefaultFont(win32con.CF_FIXEDPITCHONLY, "baseFormatFixed") + return 1 + + def OnButDefaultPropFont(self, id, code): + if code == win32con.BN_CLICKED: + self._DoButDefaultFont(win32con.CF_SCALABLEONLY, "baseFormatProp") + return 1 + + def OnButFixedOrDefault(self, id, code): + if code == win32con.BN_CLICKED: + bUseFixed = id == win32ui.IDC_RADIO1 + self.GetDlgItem(win32ui.IDC_RADIO1).GetCheck() != 0 + self.scintilla._GetColorizer().bUseFixed = bUseFixed + self.scintilla.ApplyFormattingStyles(0) + return 1 + + def OnButThisFont(self, id, code): + if code == win32con.BN_CLICKED: + flags = ( + win32con.CF_SCREENFONTS + | win32con.CF_EFFECTS + | win32con.CF_FORCEFONTEXIST + ) + style = self.GetSelectedStyle() + # If the selected style is based on the default, we need to apply + # the default to it. + def_format = self.scintilla._GetColorizer().GetDefaultFormat() + format = style.GetCompleteFormat(def_format) + d = win32ui.CreateFontDialog(format, flags, None, self) + if d.DoModal() == win32con.IDOK: + style.format = d.GetCharFormat() + self.scintilla.ApplyFormattingStyles(0) + return 1 + + def OnButUseDefaultFont(self, id, code): + if code == win32con.BN_CLICKED: + isDef = self.butIsDefault.GetCheck() + self.GetDlgItem(win32ui.IDC_BUTTON3).EnableWindow(not isDef) + if isDef: # Being reset to the default font. + style = self.GetSelectedStyle() + style.ForceAgainstDefault() + self.UpdateUIForStyle(style) + self.scintilla.ApplyFormattingStyles(0) + else: + # User wants to override default - + # do nothing! + pass + + def OnButThisBackground(self, id, code): + if code == win32con.BN_CLICKED: + style = self.GetSelectedStyle() + bg = win32api.RGB(0xFF, 0xFF, 0xFF) + if style.background != CLR_INVALID: + bg = style.background + d = win32ui.CreateColorDialog(bg, 0, self) + if d.DoModal() == win32con.IDOK: + style.background = d.GetColor() + self.scintilla.ApplyFormattingStyles(0) + return 1 + + def OnButUseDefaultBackground(self, id, code): + if code == win32con.BN_CLICKED: + isDef = self.butIsDefaultBackground.GetCheck() + self.GetDlgItem(win32ui.IDC_BUTTON4).EnableWindow(not isDef) + if isDef: # Being reset to the default color + style = self.GetSelectedStyle() + style.background = style.default_background + self.UpdateUIForStyle(style) + self.scintilla.ApplyFormattingStyles(0) + else: + # User wants to override default - + # do nothing! + pass + + def OnListCommand(self, id, code): + if code == win32con.LBN_SELCHANGE: + style = self.GetSelectedStyle() + self.UpdateUIForStyle(style) + return 1 + + def UpdateUIForStyle(self, style): + format = style.format + sel = 0 + for c in paletteVGA: + if format[4] == c[1]: + # print "Style", style.name, "is", c[0] + break + sel = sel + 1 + else: + sel = -1 + self.cbo.SetCurSel(sel) + self.butIsDefault.SetCheck(style.IsBasedOnDefault()) + self.GetDlgItem(win32ui.IDC_BUTTON3).EnableWindow(not style.IsBasedOnDefault()) + + self.butIsDefaultBackground.SetCheck( + style.background == style.default_background + ) + self.GetDlgItem(win32ui.IDC_BUTTON4).EnableWindow( + style.background != style.default_background + ) + + bold = format[1] & win32con.CFE_BOLD != 0 + italic = format[1] & win32con.CFE_ITALIC != 0 + self.cboBoldItalic.SetCurSel(bold * 2 + italic) + + def OnStyleUIChanged(self, id, code): + if code in [win32con.BN_CLICKED, win32con.CBN_SELCHANGE]: + style = self.GetSelectedStyle() + self.ApplyUIFormatToStyle(style) + self.scintilla.ApplyFormattingStyles(0) + return 0 + return 1 + + def ApplyUIFormatToStyle(self, style): + format = style.format + color = paletteVGA[self.cbo.GetCurSel()] + effect = 0 + sel = self.cboBoldItalic.GetCurSel() + if sel == 0: + effect = 0 + elif sel == 1: + effect = win32con.CFE_ITALIC + elif sel == 2: + effect = win32con.CFE_BOLD + else: + effect = win32con.CFE_BOLD | win32con.CFE_ITALIC + maskFlags = ( + format[0] | win32con.CFM_COLOR | win32con.CFM_BOLD | win32con.CFM_ITALIC + ) + style.format = ( + maskFlags, + effect, + style.format[2], + style.format[3], + color[1], + ) + style.format[5:] + + def OnOK(self): + self.scintilla._GetColorizer().SavePreferences() + return 1 + + +def test(): + page = ColorEditorPropertyPage() + sheet = pywin.mfc.dialog.PropertySheet("Test") + sheet.AddPage(page) + sheet.CreateWindow() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/scintilla/control.py b/myenv/Lib/site-packages/pythonwin/pywin/scintilla/control.py new file mode 100644 index 000000000..460b16864 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/scintilla/control.py @@ -0,0 +1,569 @@ +# An Python interface to the Scintilla control. +# +# Exposes Python classes that allow you to use Scintilla as +# a "standard" MFC edit control (eg, control.GetTextLength(), control.GetSel() +# plus many Scintilla specific features (eg control.SCIAddStyledText()) + +import array +import os +import struct + +import win32api +import win32con +import win32ui +from pywin import default_scintilla_encoding +from pywin.mfc import window + +from . import scintillacon + +# Load Scintilla.dll to get access to the control. +# We expect to find this in the same directory as win32ui.pyd +dllid = None +if win32ui.debug: # If running _d version of Pythonwin... + try: + dllid = win32api.LoadLibrary( + os.path.join(os.path.split(win32ui.__file__)[0], "Scintilla_d.DLL") + ) + except ( + win32api.error + ): # Not there - we dont _need_ a debug ver, so ignore this error. + pass +if dllid is None: + try: + dllid = win32api.LoadLibrary( + os.path.join(os.path.split(win32ui.__file__)[0], "Scintilla.DLL") + ) + except win32api.error: + pass +if dllid is None: + # Still not there - lets see if Windows can find it by searching? + dllid = win32api.LoadLibrary("Scintilla.DLL") + +# null_byte is str in py2k, bytes on py3k +null_byte = "\0".encode("ascii") + +## These are from Richedit.h - need to add to win32con or commctrl +EM_GETTEXTRANGE = 1099 +EM_EXLINEFROMCHAR = 1078 +EM_FINDTEXTEX = 1103 +EM_GETSELTEXT = 1086 +EM_EXSETSEL = win32con.WM_USER + 55 + + +class ScintillaNotification: + def __init__(self, **args): + self.__dict__.update(args) + + +class ScintillaControlInterface: + def SCIUnpackNotifyMessage(self, msg): + format = "iiiiPiiiPPiiii" + bytes = win32ui.GetBytes(msg, struct.calcsize(format)) + ( + position, + ch, + modifiers, + modificationType, + text_ptr, + length, + linesAdded, + msg, + wParam, + lParam, + line, + foldLevelNow, + foldLevelPrev, + margin, + ) = struct.unpack(format, bytes) + return ScintillaNotification( + position=position, + ch=ch, + modifiers=modifiers, + modificationType=modificationType, + text_ptr=text_ptr, + length=length, + linesAdded=linesAdded, + msg=msg, + wParam=wParam, + lParam=lParam, + line=line, + foldLevelNow=foldLevelNow, + foldLevelPrev=foldLevelPrev, + margin=margin, + ) + + def SCIAddText(self, text): + self.SendMessage( + scintillacon.SCI_ADDTEXT, text.encode(default_scintilla_encoding) + ) + + def SCIAddStyledText(self, text, style=None): + # If style is None, text is assumed to be a "native" Scintilla buffer. + # If style is specified, text is a normal string, and the style is + # assumed to apply to the entire string. + if style is not None: + text = list(map(lambda char, style=style: char + chr(style), text)) + text = "".join(text) + self.SendMessage( + scintillacon.SCI_ADDSTYLEDTEXT, text.encode(default_scintilla_encoding) + ) + + def SCIInsertText(self, text, pos=-1): + # SCIInsertText allows unicode or bytes - but if they are bytes, + # the caller must ensure it is encoded correctly. + if isinstance(text, str): + text = text.encode(default_scintilla_encoding) + self.SendScintilla(scintillacon.SCI_INSERTTEXT, pos, text + null_byte) + + def SCISetSavePoint(self): + self.SendScintilla(scintillacon.SCI_SETSAVEPOINT) + + def SCISetUndoCollection(self, collectFlag): + self.SendScintilla(scintillacon.SCI_SETUNDOCOLLECTION, collectFlag) + + def SCIBeginUndoAction(self): + self.SendScintilla(scintillacon.SCI_BEGINUNDOACTION) + + def SCIEndUndoAction(self): + self.SendScintilla(scintillacon.SCI_ENDUNDOACTION) + + def SCIGetCurrentPos(self): + return self.SendScintilla(scintillacon.SCI_GETCURRENTPOS) + + def SCIGetCharAt(self, pos): + # Must ensure char is unsigned! + return chr(self.SendScintilla(scintillacon.SCI_GETCHARAT, pos) & 0xFF) + + def SCIGotoLine(self, line): + self.SendScintilla(scintillacon.SCI_GOTOLINE, line) + + def SCIBraceMatch(self, pos, maxReStyle): + return self.SendScintilla(scintillacon.SCI_BRACEMATCH, pos, maxReStyle) + + def SCIBraceHighlight(self, pos, posOpposite): + return self.SendScintilla(scintillacon.SCI_BRACEHIGHLIGHT, pos, posOpposite) + + def SCIBraceBadHighlight(self, pos): + return self.SendScintilla(scintillacon.SCI_BRACEBADLIGHT, pos) + + #################################### + # Styling + # def SCIColourise(self, start=0, end=-1): + # NOTE - dependent on of we use builtin lexer, so handled below. + def SCIGetEndStyled(self): + return self.SendScintilla(scintillacon.SCI_GETENDSTYLED) + + def SCIStyleSetFore(self, num, v): + return self.SendScintilla(scintillacon.SCI_STYLESETFORE, num, v) + + def SCIStyleSetBack(self, num, v): + return self.SendScintilla(scintillacon.SCI_STYLESETBACK, num, v) + + def SCIStyleSetEOLFilled(self, num, v): + return self.SendScintilla(scintillacon.SCI_STYLESETEOLFILLED, num, v) + + def SCIStyleSetFont(self, num, name, characterset=0): + buff = (name + "\0").encode(default_scintilla_encoding) + self.SendScintilla(scintillacon.SCI_STYLESETFONT, num, buff) + self.SendScintilla(scintillacon.SCI_STYLESETCHARACTERSET, num, characterset) + + def SCIStyleSetBold(self, num, bBold): + self.SendScintilla(scintillacon.SCI_STYLESETBOLD, num, bBold) + + def SCIStyleSetItalic(self, num, bItalic): + self.SendScintilla(scintillacon.SCI_STYLESETITALIC, num, bItalic) + + def SCIStyleSetSize(self, num, size): + self.SendScintilla(scintillacon.SCI_STYLESETSIZE, num, size) + + def SCIGetViewWS(self): + return self.SendScintilla(scintillacon.SCI_GETVIEWWS) + + def SCISetViewWS(self, val): + self.SendScintilla(scintillacon.SCI_SETVIEWWS, not (val == 0)) + self.InvalidateRect() + + def SCISetIndentationGuides(self, val): + self.SendScintilla(scintillacon.SCI_SETINDENTATIONGUIDES, val) + + def SCIGetIndentationGuides(self): + return self.SendScintilla(scintillacon.SCI_GETINDENTATIONGUIDES) + + def SCISetIndent(self, val): + self.SendScintilla(scintillacon.SCI_SETINDENT, val) + + def SCIGetIndent(self, val): + return self.SendScintilla(scintillacon.SCI_GETINDENT) + + def SCIGetViewEOL(self): + return self.SendScintilla(scintillacon.SCI_GETVIEWEOL) + + def SCISetViewEOL(self, val): + self.SendScintilla(scintillacon.SCI_SETVIEWEOL, not (val == 0)) + self.InvalidateRect() + + def SCISetTabWidth(self, width): + self.SendScintilla(scintillacon.SCI_SETTABWIDTH, width, 0) + + def SCIStartStyling(self, pos, mask): + self.SendScintilla(scintillacon.SCI_STARTSTYLING, pos, mask) + + def SCISetStyling(self, pos, attr): + self.SendScintilla(scintillacon.SCI_SETSTYLING, pos, attr) + + def SCISetStylingEx(self, ray): # ray is an array. + address, length = ray.buffer_info() + self.SendScintilla(scintillacon.SCI_SETSTYLINGEX, length, address) + + def SCIGetStyleAt(self, pos): + return self.SendScintilla(scintillacon.SCI_GETSTYLEAT, pos) + + def SCISetMarginWidth(self, width): + self.SendScintilla(scintillacon.SCI_SETMARGINWIDTHN, 1, width) + + def SCISetMarginWidthN(self, n, width): + self.SendScintilla(scintillacon.SCI_SETMARGINWIDTHN, n, width) + + def SCISetFoldFlags(self, flags): + self.SendScintilla(scintillacon.SCI_SETFOLDFLAGS, flags) + + # Markers + def SCIMarkerDefineAll(self, markerNum, markerType, fore, back): + self.SCIMarkerDefine(markerNum, markerType) + self.SCIMarkerSetFore(markerNum, fore) + self.SCIMarkerSetBack(markerNum, back) + + def SCIMarkerDefine(self, markerNum, markerType): + self.SendScintilla(scintillacon.SCI_MARKERDEFINE, markerNum, markerType) + + def SCIMarkerSetFore(self, markerNum, fore): + self.SendScintilla(scintillacon.SCI_MARKERSETFORE, markerNum, fore) + + def SCIMarkerSetBack(self, markerNum, back): + self.SendScintilla(scintillacon.SCI_MARKERSETBACK, markerNum, back) + + def SCIMarkerAdd(self, lineNo, markerNum): + self.SendScintilla(scintillacon.SCI_MARKERADD, lineNo, markerNum) + + def SCIMarkerDelete(self, lineNo, markerNum): + self.SendScintilla(scintillacon.SCI_MARKERDELETE, lineNo, markerNum) + + def SCIMarkerDeleteAll(self, markerNum=-1): + self.SendScintilla(scintillacon.SCI_MARKERDELETEALL, markerNum) + + def SCIMarkerGet(self, lineNo): + return self.SendScintilla(scintillacon.SCI_MARKERGET, lineNo) + + def SCIMarkerNext(self, lineNo, markerNum): + return self.SendScintilla(scintillacon.SCI_MARKERNEXT, lineNo, markerNum) + + def SCICancel(self): + self.SendScintilla(scintillacon.SCI_CANCEL) + + # AutoComplete + def SCIAutoCShow(self, text): + if type(text) in [type([]), type(())]: + text = " ".join(text) + buff = (text + "\0").encode(default_scintilla_encoding) + return self.SendScintilla(scintillacon.SCI_AUTOCSHOW, 0, buff) + + def SCIAutoCCancel(self): + self.SendScintilla(scintillacon.SCI_AUTOCCANCEL) + + def SCIAutoCActive(self): + return self.SendScintilla(scintillacon.SCI_AUTOCACTIVE) + + def SCIAutoCComplete(self): + return self.SendScintilla(scintillacon.SCI_AUTOCCOMPLETE) + + def SCIAutoCStops(self, stops): + buff = (stops + "\0").encode(default_scintilla_encoding) + self.SendScintilla(scintillacon.SCI_AUTOCSTOPS, 0, buff) + + def SCIAutoCSetAutoHide(self, hide): + self.SendScintilla(scintillacon.SCI_AUTOCSETAUTOHIDE, hide) + + def SCIAutoCSetFillups(self, fillups): + self.SendScintilla(scintillacon.SCI_AUTOCSETFILLUPS, fillups) + + # Call tips + def SCICallTipShow(self, text, pos=-1): + if pos == -1: + pos = self.GetSel()[0] + buff = (text + "\0").encode(default_scintilla_encoding) + self.SendScintilla(scintillacon.SCI_CALLTIPSHOW, pos, buff) + + def SCICallTipCancel(self): + self.SendScintilla(scintillacon.SCI_CALLTIPCANCEL) + + def SCICallTipActive(self): + return self.SendScintilla(scintillacon.SCI_CALLTIPACTIVE) + + def SCICallTipPosStart(self): + return self.SendScintilla(scintillacon.SCI_CALLTIPPOSSTART) + + def SCINewline(self): + self.SendScintilla(scintillacon.SCI_NEWLINE) + + # Lexer etc + def SCISetKeywords(self, keywords, kw_list_no=0): + buff = (keywords + "\0").encode(default_scintilla_encoding) + self.SendScintilla(scintillacon.SCI_SETKEYWORDS, kw_list_no, buff) + + def SCISetProperty(self, name, value): + name_buff = array.array("b", (name + "\0").encode(default_scintilla_encoding)) + val_buff = array.array( + "b", (str(value) + "\0").encode(default_scintilla_encoding) + ) + address_name_buffer = name_buff.buffer_info()[0] + address_val_buffer = val_buff.buffer_info()[0] + self.SendScintilla( + scintillacon.SCI_SETPROPERTY, address_name_buffer, address_val_buffer + ) + + def SCISetStyleBits(self, nbits): + self.SendScintilla(scintillacon.SCI_SETSTYLEBITS, nbits) + + # Folding + def SCIGetFoldLevel(self, lineno): + return self.SendScintilla(scintillacon.SCI_GETFOLDLEVEL, lineno) + + def SCIToggleFold(self, lineno): + return self.SendScintilla(scintillacon.SCI_TOGGLEFOLD, lineno) + + def SCIEnsureVisible(self, lineno): + self.SendScintilla(scintillacon.SCI_ENSUREVISIBLE, lineno) + + def SCIGetFoldExpanded(self, lineno): + return self.SendScintilla(scintillacon.SCI_GETFOLDEXPANDED, lineno) + + # right edge + def SCISetEdgeColumn(self, edge): + self.SendScintilla(scintillacon.SCI_SETEDGECOLUMN, edge) + + def SCIGetEdgeColumn(self): + return self.SendScintilla(scintillacon.SCI_GETEDGECOLUMN) + + def SCISetEdgeMode(self, mode): + self.SendScintilla(scintillacon.SCI_SETEDGEMODE, mode) + + def SCIGetEdgeMode(self): + return self.SendScintilla(scintillacon.SCI_GETEDGEMODE) + + def SCISetEdgeColor(self, color): + self.SendScintilla(scintillacon.SCI_SETEDGECOLOUR, color) + + def SCIGetEdgeColor(self): + return self.SendScintilla(scintillacon.SCI_GETEDGECOLOR) + + # Multi-doc + def SCIGetDocPointer(self): + return self.SendScintilla(scintillacon.SCI_GETDOCPOINTER) + + def SCISetDocPointer(self, p): + return self.SendScintilla(scintillacon.SCI_SETDOCPOINTER, 0, p) + + def SCISetWrapMode(self, mode): + return self.SendScintilla(scintillacon.SCI_SETWRAPMODE, mode) + + def SCIGetWrapMode(self): + return self.SendScintilla(scintillacon.SCI_GETWRAPMODE) + + +class CScintillaEditInterface(ScintillaControlInterface): + def close(self): + self.colorizer = None + + def Clear(self): + self.SendScintilla(win32con.WM_CLEAR) + + def FindText(self, flags, range, findText): + """LPARAM for EM_FINDTEXTEX: + typedef struct _findtextex { + CHARRANGE chrg; + LPCTSTR lpstrText; + CHARRANGE chrgText;} FINDTEXTEX; + typedef struct _charrange { + LONG cpMin; + LONG cpMax;} CHARRANGE; + """ + findtextex_fmt = "llPll" + ## Scintilla does not handle unicode in EM_FINDTEXT msg (FINDTEXTEX struct) + txt_buff = (findText + "\0").encode(default_scintilla_encoding) + txt_array = array.array("b", txt_buff) + ft_buff = struct.pack( + findtextex_fmt, range[0], range[1], txt_array.buffer_info()[0], 0, 0 + ) + ft_array = array.array("b", ft_buff) + rc = self.SendScintilla(EM_FINDTEXTEX, flags, ft_array.buffer_info()[0]) + ftUnpacked = struct.unpack(findtextex_fmt, ft_array) + return rc, (ftUnpacked[3], ftUnpacked[4]) + + def GetSel(self): + currentPos = self.SendScintilla(scintillacon.SCI_GETCURRENTPOS) + anchorPos = self.SendScintilla(scintillacon.SCI_GETANCHOR) + if currentPos < anchorPos: + return (currentPos, anchorPos) + else: + return (anchorPos, currentPos) + return currentPos + + def GetSelText(self): + start, end = self.GetSel() + txtBuf = array.array("b", null_byte * (end - start + 1)) + addressTxtBuf = txtBuf.buffer_info()[0] + # EM_GETSELTEXT is documented as returning the number of chars + # not including the NULL, but scintilla includes the NULL. A + # quick glance at the scintilla impl doesn't make this + # obvious - the NULL is included in the 'selection' object + # and reflected in the length of that 'selection' object. + # I expect that is a bug in scintilla and may be fixed by now, + # but we just blindly assume that the last char is \0 and + # strip it. + self.SendScintilla(EM_GETSELTEXT, 0, addressTxtBuf) + return txtBuf.tobytes()[:-1].decode(default_scintilla_encoding) + + def SetSel(self, start=0, end=None): + if type(start) == type(()): + assert ( + end is None + ), "If you pass a point in the first param, the second must be None" + start, end = start + elif end is None: + end = start + if start < 0: + start = self.GetTextLength() + if end < 0: + end = self.GetTextLength() + assert start <= self.GetTextLength(), "The start postion is invalid (%d/%d)" % ( + start, + self.GetTextLength(), + ) + assert end <= self.GetTextLength(), "The end postion is invalid (%d/%d)" % ( + end, + self.GetTextLength(), + ) + cr = struct.pack("ll", start, end) + crBuff = array.array("b", cr) + addressCrBuff = crBuff.buffer_info()[0] + rc = self.SendScintilla(EM_EXSETSEL, 0, addressCrBuff) + + def GetLineCount(self): + return self.SendScintilla(win32con.EM_GETLINECOUNT) + + def LineFromChar(self, charPos=-1): + if charPos == -1: + charPos = self.GetSel()[0] + assert ( + charPos >= 0 and charPos <= self.GetTextLength() + ), "The charPos postion (%s) is invalid (max=%s)" % ( + charPos, + self.GetTextLength(), + ) + # return self.SendScintilla(EM_EXLINEFROMCHAR, charPos) + # EM_EXLINEFROMCHAR puts charPos in lParam, not wParam + return self.SendScintilla(EM_EXLINEFROMCHAR, 0, charPos) + + def LineIndex(self, line): + return self.SendScintilla(win32con.EM_LINEINDEX, line) + + def ScrollCaret(self): + return self.SendScintilla(win32con.EM_SCROLLCARET) + + def GetCurLineNumber(self): + return self.LineFromChar(self.SCIGetCurrentPos()) + + def GetTextLength(self): + return self.SendScintilla(scintillacon.SCI_GETTEXTLENGTH) + + def GetTextRange(self, start=0, end=-1, decode=True): + if end == -1: + end = self.SendScintilla(scintillacon.SCI_GETTEXTLENGTH) + assert end >= start, "Negative index requested (%d/%d)" % (start, end) + assert ( + start >= 0 and start <= self.GetTextLength() + ), "The start postion is invalid" + assert end >= 0 and end <= self.GetTextLength(), "The end postion is invalid" + initer = null_byte * (end - start + 1) + buff = array.array("b", initer) + addressBuffer = buff.buffer_info()[0] + tr = struct.pack("llP", start, end, addressBuffer) + trBuff = array.array("b", tr) + addressTrBuff = trBuff.buffer_info()[0] + num_bytes = self.SendScintilla(EM_GETTEXTRANGE, 0, addressTrBuff) + ret = buff.tobytes()[:num_bytes] + if decode: + ret = ret.decode(default_scintilla_encoding) + return ret + + def ReplaceSel(self, str): + buff = (str + "\0").encode(default_scintilla_encoding) + self.SendScintilla(scintillacon.SCI_REPLACESEL, 0, buff) + + def GetLine(self, line=-1): + if line == -1: + line = self.GetCurLineNumber() + start = self.LineIndex(line) + end = self.LineIndex(line + 1) + return self.GetTextRange(start, end) + + def SetReadOnly(self, flag=1): + return self.SendScintilla(win32con.EM_SETREADONLY, flag) + + def LineScroll(self, lines, cols=0): + return self.SendScintilla(win32con.EM_LINESCROLL, cols, lines) + + def GetFirstVisibleLine(self): + return self.SendScintilla(win32con.EM_GETFIRSTVISIBLELINE) + + def SetWordWrap(self, mode): + if mode != win32ui.CRichEditView_WrapNone: + raise ValueError("We dont support word-wrap (I dont think :-)") + + +class CScintillaColorEditInterface(CScintillaEditInterface): + ################################ + # Plug-in colorizer support + def _GetColorizer(self): + if not hasattr(self, "colorizer"): + self.colorizer = self._MakeColorizer() + return self.colorizer + + def _MakeColorizer(self): + # Give parent a chance to hook. + parent_func = getattr(self.GetParentFrame(), "_MakeColorizer", None) + if parent_func is not None: + return parent_func() + from . import formatter + + ## return formatter.PythonSourceFormatter(self) + return formatter.BuiltinPythonSourceFormatter(self) + + def Colorize(self, start=0, end=-1): + c = self._GetColorizer() + if c is not None: + c.Colorize(start, end) + + def ApplyFormattingStyles(self, bReload=1): + c = self._GetColorizer() + if c is not None: + c.ApplyFormattingStyles(bReload) + + # The Parent window will normally hook + def HookFormatter(self, parent=None): + c = self._GetColorizer() + if c is not None: # No need if we have no color! + c.HookFormatter(parent) + + +class CScintillaEdit(window.Wnd, CScintillaColorEditInterface): + def __init__(self, wnd=None): + if wnd is None: + wnd = win32ui.CreateWnd() + window.Wnd.__init__(self, wnd) + + def SendScintilla(self, msg, w=0, l=0): + return self.SendMessage(msg, w, l) + + def CreateWindow(self, style, rect, parent, id): + self._obj_.CreateWindow("Scintilla", "Scintilla", style, rect, parent, id, None) diff --git a/myenv/Lib/site-packages/pythonwin/pywin/scintilla/document.py b/myenv/Lib/site-packages/pythonwin/pywin/scintilla/document.py new file mode 100644 index 000000000..cddb442c1 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/scintilla/document.py @@ -0,0 +1,312 @@ +import codecs +import re +import string + +import win32con +import win32ui +from pywin import default_scintilla_encoding +from pywin.mfc import docview + +from . import scintillacon + +crlf_bytes = "\r\n".encode("ascii") +lf_bytes = "\n".encode("ascii") + +# re from pep263 - but we use it both on bytes and strings. +re_encoding_bytes = re.compile("coding[:=]\s*([-\w.]+)".encode("ascii")) +re_encoding_text = re.compile("coding[:=]\s*([-\w.]+)") + +ParentScintillaDocument = docview.Document + + +class CScintillaDocument(ParentScintillaDocument): + "A SyntEdit document." + + def __init__(self, *args): + self.bom = None # the BOM, if any, read from the file. + # the encoding we detected from the source. Might have + # detected via the BOM or an encoding decl. Note that in + # the latter case (ie, while self.bom is None), it can't be + # trusted - the user may have edited the encoding decl between + # open and save. + self.source_encoding = None + ParentScintillaDocument.__init__(self, *args) + + def DeleteContents(self): + pass + + def OnOpenDocument(self, filename): + # init data members + # print "Opening", filename + self.SetPathName(filename) # Must set this early! + try: + # load the text as binary we can get smart + # about detecting any existing EOL conventions. + f = open(filename, "rb") + try: + self._LoadTextFromFile(f) + finally: + f.close() + except IOError: + rc = win32ui.MessageBox( + "Could not load the file from %s\n\nDo you want to create a new file?" + % filename, + "Pythonwin", + win32con.MB_YESNO | win32con.MB_ICONWARNING, + ) + if rc == win32con.IDNO: + return 0 + assert rc == win32con.IDYES, rc + try: + f = open(filename, "wb+") + try: + self._LoadTextFromFile(f) + finally: + f.close() + except IOError as e: + rc = win32ui.MessageBox("Cannot create the file %s" % filename) + return 1 + + def SaveFile(self, fileName, encoding=None): + view = self.GetFirstView() + ok = view.SaveTextFile(fileName, encoding=encoding) + if ok: + view.SCISetSavePoint() + return ok + + def ApplyFormattingStyles(self): + self._ApplyOptionalToViews("ApplyFormattingStyles") + + # ##################### + # File related functions + # Helper to transfer text from the MFC document to the control. + def _LoadTextFromFile(self, f): + # detect EOL mode - we don't support \r only - so find the + # first '\n' and guess based on the char before. + l = f.readline() + l2 = f.readline() + # If line ends with \r\n or has no line ending, use CRLF. + if l.endswith(crlf_bytes) or not l.endswith(lf_bytes): + eol_mode = scintillacon.SC_EOL_CRLF + else: + eol_mode = scintillacon.SC_EOL_LF + + # Detect the encoding - first look for a BOM, and if not found, + # look for a pep263 encoding declaration. + for bom, encoding in ( + (codecs.BOM_UTF8, "utf8"), + (codecs.BOM_UTF16_LE, "utf_16_le"), + (codecs.BOM_UTF16_BE, "utf_16_be"), + ): + if l.startswith(bom): + self.bom = bom + self.source_encoding = encoding + l = l[len(bom) :] # remove it. + break + else: + # no bom detected - look for pep263 encoding decl. + for look in (l, l2): + # Note we are looking at raw bytes here: so + # both the re itself uses bytes and the result + # is bytes - but we need the result as a string. + match = re_encoding_bytes.search(look) + if match is not None: + self.source_encoding = match.group(1).decode("ascii") + break + + # reading by lines would be too slow? Maybe we can use the + # incremental encoders? For now just stick with loading the + # entire file in memory. + text = l + l2 + f.read() + + # Translate from source encoding to UTF-8 bytes for Scintilla + source_encoding = self.source_encoding + # If we don't know an encoding, try utf-8 - if that fails we will + # fallback to latin-1 to treat it as bytes... + if source_encoding is None: + source_encoding = "utf-8" + # we could optimize this by avoiding utf8 to-ing and from-ing, + # but then we would lose the ability to handle invalid utf8 + # (and even then, the use of encoding aliases makes this tricky) + # To create an invalid utf8 file: + # >>> open(filename, "wb").write(codecs.BOM_UTF8+"bad \xa9har\r\n") + try: + dec = text.decode(source_encoding) + except UnicodeError: + print( + "WARNING: Failed to decode bytes from '%s' encoding - treating as latin1" + % source_encoding + ) + dec = text.decode("latin1") + except LookupError: + print( + "WARNING: Invalid encoding '%s' specified - treating as latin1" + % source_encoding + ) + dec = text.decode("latin1") + # and put it back as utf8 - this shouldn't fail. + text = dec.encode(default_scintilla_encoding) + + view = self.GetFirstView() + if view.IsWindow(): + # Turn off undo collection while loading + view.SendScintilla(scintillacon.SCI_SETUNDOCOLLECTION, 0, 0) + # Make sure the control isnt read-only + view.SetReadOnly(0) + view.SendScintilla(scintillacon.SCI_CLEARALL) + view.SendMessage(scintillacon.SCI_ADDTEXT, text) + view.SendScintilla(scintillacon.SCI_SETUNDOCOLLECTION, 1, 0) + view.SendScintilla(win32con.EM_EMPTYUNDOBUFFER, 0, 0) + # set EOL mode + view.SendScintilla(scintillacon.SCI_SETEOLMODE, eol_mode) + + def _SaveTextToFile(self, view, filename, encoding=None): + s = view.GetTextRange() # already decoded from scintilla's encoding + source_encoding = encoding + if source_encoding is None: + if self.bom: + source_encoding = self.source_encoding + else: + # no BOM - look for an encoding. + bits = re.split("[\r\n]+", s, 3) + for look in bits[:-1]: + match = re_encoding_text.search(look) + if match is not None: + source_encoding = match.group(1) + self.source_encoding = source_encoding + break + + if source_encoding is None: + source_encoding = "utf-8" + + ## encode data before opening file so script is not lost if encoding fails + file_contents = s.encode(source_encoding) + # Open in binary mode as scintilla itself ensures the + # line endings are already appropriate + f = open(filename, "wb") + try: + if self.bom: + f.write(self.bom) + f.write(file_contents) + finally: + f.close() + self.SetModifiedFlag(0) + + def FinalizeViewCreation(self, view): + pass + + def HookViewNotifications(self, view): + parent = view.GetParentFrame() + parent.HookNotify( + ViewNotifyDelegate(self, "OnBraceMatch"), scintillacon.SCN_CHECKBRACE + ) + parent.HookNotify( + ViewNotifyDelegate(self, "OnMarginClick"), scintillacon.SCN_MARGINCLICK + ) + parent.HookNotify( + ViewNotifyDelegate(self, "OnNeedShown"), scintillacon.SCN_NEEDSHOWN + ) + + parent.HookNotify( + DocumentNotifyDelegate(self, "OnSavePointReached"), + scintillacon.SCN_SAVEPOINTREACHED, + ) + parent.HookNotify( + DocumentNotifyDelegate(self, "OnSavePointLeft"), + scintillacon.SCN_SAVEPOINTLEFT, + ) + parent.HookNotify( + DocumentNotifyDelegate(self, "OnModifyAttemptRO"), + scintillacon.SCN_MODIFYATTEMPTRO, + ) + # Tell scintilla what characters should abort auto-complete. + view.SCIAutoCStops(string.whitespace + "()[]:;+-/*=\\?'!#@$%^&,<>\"'|") + + if view != self.GetFirstView(): + view.SCISetDocPointer(self.GetFirstView().SCIGetDocPointer()) + + def OnSavePointReached(self, std, extra): + self.SetModifiedFlag(0) + + def OnSavePointLeft(self, std, extra): + self.SetModifiedFlag(1) + + def OnModifyAttemptRO(self, std, extra): + self.MakeDocumentWritable() + + # All Marker functions are 1 based. + def MarkerAdd(self, lineNo, marker): + self.GetEditorView().SCIMarkerAdd(lineNo - 1, marker) + + def MarkerCheck(self, lineNo, marker): + v = self.GetEditorView() + lineNo = lineNo - 1 # Make 0 based + markerState = v.SCIMarkerGet(lineNo) + return markerState & (1 << marker) != 0 + + def MarkerToggle(self, lineNo, marker): + v = self.GetEditorView() + if self.MarkerCheck(lineNo, marker): + v.SCIMarkerDelete(lineNo - 1, marker) + else: + v.SCIMarkerAdd(lineNo - 1, marker) + + def MarkerDelete(self, lineNo, marker): + self.GetEditorView().SCIMarkerDelete(lineNo - 1, marker) + + def MarkerDeleteAll(self, marker): + self.GetEditorView().SCIMarkerDeleteAll(marker) + + def MarkerGetNext(self, lineNo, marker): + return self.GetEditorView().SCIMarkerNext(lineNo - 1, 1 << marker) + 1 + + def MarkerAtLine(self, lineNo, marker): + markerState = self.GetEditorView().SCIMarkerGet(lineNo - 1) + return markerState & (1 << marker) + + # Helper for reflecting functions to views. + def _ApplyToViews(self, funcName, *args): + for view in self.GetAllViews(): + func = getattr(view, funcName) + func(*args) + + def _ApplyOptionalToViews(self, funcName, *args): + for view in self.GetAllViews(): + func = getattr(view, funcName, None) + if func is not None: + func(*args) + + def GetEditorView(self): + # Find the first frame with a view, + # then ask it to give the editor view + # as it knows which one is "active" + try: + frame_gev = self.GetFirstView().GetParentFrame().GetEditorView + except AttributeError: + return self.GetFirstView() + return frame_gev() + + +# Delegate to the correct view, based on the control that sent it. +class ViewNotifyDelegate: + def __init__(self, doc, name): + self.doc = doc + self.name = name + + def __call__(self, std, extra): + (hwndFrom, idFrom, code) = std + for v in self.doc.GetAllViews(): + if v.GetSafeHwnd() == hwndFrom: + return getattr(v, self.name)(*(std, extra)) + + +# Delegate to the document, but only from a single view (as each view sends it seperately) +class DocumentNotifyDelegate: + def __init__(self, doc, name): + self.doc = doc + self.delegate = getattr(doc, name) + + def __call__(self, std, extra): + (hwndFrom, idFrom, code) = std + if hwndFrom == self.doc.GetEditorView().GetSafeHwnd(): + self.delegate(*(std, extra)) diff --git a/myenv/Lib/site-packages/pythonwin/pywin/scintilla/find.py b/myenv/Lib/site-packages/pythonwin/pywin/scintilla/find.py new file mode 100644 index 000000000..e1d21a5bc --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/scintilla/find.py @@ -0,0 +1,510 @@ +# find.py - Find and Replace +import afxres +import win32api +import win32con +import win32ui +from pywin.framework import scriptutils +from pywin.mfc import dialog + +FOUND_NOTHING = 0 +FOUND_NORMAL = 1 +FOUND_LOOPED_BACK = 2 +FOUND_NEXT_FILE = 3 + + +class SearchParams: + def __init__(self, other=None): + if other is None: + self.__dict__["findText"] = "" + self.__dict__["replaceText"] = "" + self.__dict__["matchCase"] = 0 + self.__dict__["matchWords"] = 0 + self.__dict__["acrossFiles"] = 0 + self.__dict__["remember"] = 1 + self.__dict__["sel"] = (-1, -1) + self.__dict__["keepDialogOpen"] = 0 + else: + self.__dict__.update(other.__dict__) + + # Helper so we cant misspell attributes :-) + def __setattr__(self, attr, val): + if not hasattr(self, attr): + raise AttributeError(attr) + self.__dict__[attr] = val + + +curDialog = None +lastSearch = defaultSearch = SearchParams() +searchHistory = [] + + +def ShowFindDialog(): + _ShowDialog(FindDialog) + + +def ShowReplaceDialog(): + _ShowDialog(ReplaceDialog) + + +def _ShowDialog(dlgClass): + global curDialog + if curDialog is not None: + if curDialog.__class__ != dlgClass: + curDialog.DestroyWindow() + curDialog = None + else: + curDialog.SetFocus() + if curDialog is None: + curDialog = dlgClass() + curDialog.CreateWindow() + + +def FindNext(): + params = SearchParams(lastSearch) + params.sel = (-1, -1) + if not params.findText: + ShowFindDialog() + else: + return _FindIt(None, params) + + +def _GetControl(control=None): + if control is None: + control = scriptutils.GetActiveEditControl() + return control + + +def _FindIt(control, searchParams): + global lastSearch, defaultSearch + control = _GetControl(control) + if control is None: + return FOUND_NOTHING + + # Move to the next char, so we find the next one. + flags = 0 + if searchParams.matchWords: + flags = flags | win32con.FR_WHOLEWORD + if searchParams.matchCase: + flags = flags | win32con.FR_MATCHCASE + if searchParams.sel == (-1, -1): + sel = control.GetSel() + # If the position is the same as we found last time, + # then we assume it is a "FindNext" + if sel == lastSearch.sel: + sel = sel[0] + 1, sel[0] + 1 + else: + sel = searchParams.sel + + if sel[0] == sel[1]: + sel = sel[0], control.GetTextLength() + + rc = FOUND_NOTHING + # (Old edit control will fail here!) + posFind, foundSel = control.FindText(flags, sel, searchParams.findText) + lastSearch = SearchParams(searchParams) + if posFind >= 0: + rc = FOUND_NORMAL + lineno = control.LineFromChar(posFind) + control.SCIEnsureVisible(lineno) + control.SetSel(foundSel) + control.SetFocus() + win32ui.SetStatusText(win32ui.LoadString(afxres.AFX_IDS_IDLEMESSAGE)) + if rc == FOUND_NOTHING and lastSearch.acrossFiles: + # Loop around all documents. First find this document. + try: + try: + doc = control.GetDocument() + except AttributeError: + try: + doc = control.GetParent().GetDocument() + except AttributeError: + print("Cant find a document for the control!") + doc = None + if doc is not None: + template = doc.GetDocTemplate() + alldocs = template.GetDocumentList() + mypos = lookpos = alldocs.index(doc) + while 1: + lookpos = (lookpos + 1) % len(alldocs) + if lookpos == mypos: + break + view = alldocs[lookpos].GetFirstView() + posFind, foundSel = view.FindText( + flags, (0, view.GetTextLength()), searchParams.findText + ) + if posFind >= 0: + nChars = foundSel[1] - foundSel[0] + lineNo = view.LineFromChar(posFind) # zero based. + lineStart = view.LineIndex(lineNo) + colNo = posFind - lineStart # zero based. + scriptutils.JumpToDocument( + alldocs[lookpos].GetPathName(), + lineNo + 1, + colNo + 1, + nChars, + ) + rc = FOUND_NEXT_FILE + break + except win32ui.error: + pass + if rc == FOUND_NOTHING: + # Loop around this control - attempt to find from the start of the control. + posFind, foundSel = control.FindText( + flags, (0, sel[0] - 1), searchParams.findText + ) + if posFind >= 0: + control.SCIEnsureVisible(control.LineFromChar(foundSel[0])) + control.SetSel(foundSel) + control.SetFocus() + win32ui.SetStatusText("Not found! Searching from the top of the file.") + rc = FOUND_LOOPED_BACK + else: + lastSearch.sel = -1, -1 + win32ui.SetStatusText("Can not find '%s'" % searchParams.findText) + + if rc != FOUND_NOTHING: + lastSearch.sel = foundSel + + if lastSearch.remember: + defaultSearch = lastSearch + + # track search history + try: + ix = searchHistory.index(searchParams.findText) + except ValueError: + if len(searchHistory) > 50: + searchHistory[50:] = [] + else: + del searchHistory[ix] + searchHistory.insert(0, searchParams.findText) + + return rc + + +def _ReplaceIt(control): + control = _GetControl(control) + statusText = "Can not find '%s'." % lastSearch.findText + rc = FOUND_NOTHING + if control is not None and lastSearch.sel != (-1, -1): + control.ReplaceSel(lastSearch.replaceText) + rc = FindNext() + if rc != FOUND_NOTHING: + statusText = win32ui.LoadString(afxres.AFX_IDS_IDLEMESSAGE) + win32ui.SetStatusText(statusText) + return rc + + +class FindReplaceDialog(dialog.Dialog): + def __init__(self): + dialog.Dialog.__init__(self, self._GetDialogTemplate()) + self.HookCommand(self.OnFindNext, 109) + + def OnInitDialog(self): + self.editFindText = self.GetDlgItem(102) + self.butMatchWords = self.GetDlgItem(105) + self.butMatchCase = self.GetDlgItem(107) + self.butKeepDialogOpen = self.GetDlgItem(115) + self.butAcrossFiles = self.GetDlgItem(116) + self.butRemember = self.GetDlgItem(117) + + self.editFindText.SetWindowText(defaultSearch.findText) + control = _GetControl() + # _GetControl only gets normal MDI windows; if the interactive + # window is docked and no document open, we get None. + if control: + # If we have a selection, default to that. + sel = control.GetSelText() + if len(sel) != 0: + self.editFindText.SetWindowText(sel) + if defaultSearch.remember: + defaultSearch.findText = sel + for hist in searchHistory: + self.editFindText.AddString(hist) + + if hasattr(self.editFindText, "SetEditSel"): + self.editFindText.SetEditSel(0, -1) + else: + self.editFindText.SetSel(0, -1) + self.butMatchWords.SetCheck(defaultSearch.matchWords) + self.butMatchCase.SetCheck(defaultSearch.matchCase) + self.butKeepDialogOpen.SetCheck(defaultSearch.keepDialogOpen) + self.butAcrossFiles.SetCheck(defaultSearch.acrossFiles) + self.butRemember.SetCheck(defaultSearch.remember) + return dialog.Dialog.OnInitDialog(self) + + def OnDestroy(self, msg): + global curDialog + curDialog = None + return dialog.Dialog.OnDestroy(self, msg) + + def DoFindNext(self): + params = SearchParams() + params.findText = self.editFindText.GetWindowText() + params.matchCase = self.butMatchCase.GetCheck() + params.matchWords = self.butMatchWords.GetCheck() + params.acrossFiles = self.butAcrossFiles.GetCheck() + params.remember = self.butRemember.GetCheck() + return _FindIt(None, params) + + def OnFindNext(self, id, code): + if code != 0: # BN_CLICKED + # 3d controls (python.exe + start_pythonwin.pyw) send + # other notification codes + return 1 # + if not self.editFindText.GetWindowText(): + win32api.MessageBeep() + return 1 + if self.DoFindNext() != FOUND_NOTHING: + if not self.butKeepDialogOpen.GetCheck(): + self.DestroyWindow() + + +class FindDialog(FindReplaceDialog): + def _GetDialogTemplate(self): + style = ( + win32con.DS_MODALFRAME + | win32con.WS_POPUP + | win32con.WS_VISIBLE + | win32con.WS_CAPTION + | win32con.WS_SYSMENU + | win32con.DS_SETFONT + ) + visible = win32con.WS_CHILD | win32con.WS_VISIBLE + dt = [ + ["Find", (0, 2, 240, 75), style, None, (8, "MS Sans Serif")], + ["Static", "Fi&nd What:", 101, (5, 8, 40, 10), visible], + [ + "ComboBox", + "", + 102, + (50, 7, 120, 120), + visible + | win32con.WS_BORDER + | win32con.WS_TABSTOP + | win32con.WS_VSCROLL + | win32con.CBS_DROPDOWN + | win32con.CBS_AUTOHSCROLL, + ], + [ + "Button", + "Match &whole word only", + 105, + (5, 23, 100, 10), + visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP, + ], + [ + "Button", + "Match &case", + 107, + (5, 33, 100, 10), + visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP, + ], + [ + "Button", + "Keep &dialog open", + 115, + (5, 43, 100, 10), + visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP, + ], + [ + "Button", + "Across &open files", + 116, + (5, 52, 100, 10), + visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP, + ], + [ + "Button", + "&Remember as default search", + 117, + (5, 61, 150, 10), + visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP, + ], + [ + "Button", + "&Find Next", + 109, + (185, 5, 50, 14), + visible | win32con.BS_DEFPUSHBUTTON | win32con.WS_TABSTOP, + ], + [ + "Button", + "Cancel", + win32con.IDCANCEL, + (185, 23, 50, 14), + visible | win32con.WS_TABSTOP, + ], + ] + return dt + + +class ReplaceDialog(FindReplaceDialog): + def _GetDialogTemplate(self): + style = ( + win32con.DS_MODALFRAME + | win32con.WS_POPUP + | win32con.WS_VISIBLE + | win32con.WS_CAPTION + | win32con.WS_SYSMENU + | win32con.DS_SETFONT + ) + visible = win32con.WS_CHILD | win32con.WS_VISIBLE + dt = [ + ["Replace", (0, 2, 240, 95), style, 0, (8, "MS Sans Serif")], + ["Static", "Fi&nd What:", 101, (5, 8, 40, 10), visible], + [ + "ComboBox", + "", + 102, + (60, 7, 110, 120), + visible + | win32con.WS_BORDER + | win32con.WS_TABSTOP + | win32con.WS_VSCROLL + | win32con.CBS_DROPDOWN + | win32con.CBS_AUTOHSCROLL, + ], + ["Static", "Re&place with:", 103, (5, 25, 50, 10), visible], + [ + "ComboBox", + "", + 104, + (60, 24, 110, 120), + visible + | win32con.WS_BORDER + | win32con.WS_TABSTOP + | win32con.WS_VSCROLL + | win32con.CBS_DROPDOWN + | win32con.CBS_AUTOHSCROLL, + ], + [ + "Button", + "Match &whole word only", + 105, + (5, 42, 100, 10), + visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP, + ], + [ + "Button", + "Match &case", + 107, + (5, 52, 100, 10), + visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP, + ], + [ + "Button", + "Keep &dialog open", + 115, + (5, 62, 100, 10), + visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP, + ], + [ + "Button", + "Across &open files", + 116, + (5, 72, 100, 10), + visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP, + ], + [ + "Button", + "&Remember as default search", + 117, + (5, 81, 150, 10), + visible | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP, + ], + [ + "Button", + "&Find Next", + 109, + (185, 5, 50, 14), + visible | win32con.BS_DEFPUSHBUTTON | win32con.WS_TABSTOP, + ], + [ + "Button", + "&Replace", + 110, + (185, 23, 50, 14), + visible | win32con.WS_TABSTOP, + ], + [ + "Button", + "Replace &All", + 111, + (185, 41, 50, 14), + visible | win32con.WS_TABSTOP, + ], + [ + "Button", + "Cancel", + win32con.IDCANCEL, + (185, 59, 50, 14), + visible | win32con.WS_TABSTOP, + ], + ] + return dt + + def OnInitDialog(self): + rc = FindReplaceDialog.OnInitDialog(self) + self.HookCommand(self.OnReplace, 110) + self.HookCommand(self.OnReplaceAll, 111) + self.HookMessage(self.OnActivate, win32con.WM_ACTIVATE) + self.editReplaceText = self.GetDlgItem(104) + self.editReplaceText.SetWindowText(lastSearch.replaceText) + if hasattr(self.editReplaceText, "SetEditSel"): + self.editReplaceText.SetEditSel(0, -1) + else: + self.editReplaceText.SetSel(0, -1) + self.butReplace = self.GetDlgItem(110) + self.butReplaceAll = self.GetDlgItem(111) + self.CheckButtonStates() + return rc # 0 when focus set + + def CheckButtonStates(self): + # We can do a "Replace" or "Replace All" if the current selection + # is the same as the search text. + ft = self.editFindText.GetWindowText() + control = _GetControl() + # bCanReplace = len(ft)>0 and control.GetSelText() == ft + bCanReplace = control is not None and lastSearch.sel == control.GetSel() + self.butReplace.EnableWindow(bCanReplace) + + # self.butReplaceAll.EnableWindow(bCanReplace) + + def OnActivate(self, msg): + wparam = msg[2] + fActive = win32api.LOWORD(wparam) + if fActive != win32con.WA_INACTIVE: + self.CheckButtonStates() + + def OnFindNext(self, id, code): + if code != 0: + return 1 + self.DoFindNext() + self.CheckButtonStates() + + def OnReplace(self, id, code): + if code != 0: + return 1 + lastSearch.replaceText = self.editReplaceText.GetWindowText() + _ReplaceIt(None) + + def OnReplaceAll(self, id, code): + if code != 0: + return 1 + control = _GetControl(None) + if control is not None: + control.SetSel(0) + num = 0 + if self.DoFindNext() == FOUND_NORMAL: + num = 1 + lastSearch.replaceText = self.editReplaceText.GetWindowText() + while _ReplaceIt(control) == FOUND_NORMAL: + num = num + 1 + + win32ui.SetStatusText("Replaced %d occurrences" % num) + if num > 0 and not self.butKeepDialogOpen.GetCheck(): + self.DestroyWindow() + + +if __name__ == "__main__": + ShowFindDialog() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/scintilla/formatter.py b/myenv/Lib/site-packages/pythonwin/pywin/scintilla/formatter.py new file mode 100644 index 000000000..56100ccfc --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/scintilla/formatter.py @@ -0,0 +1,695 @@ +# Does Python source formatting for Scintilla controls. +import array +import string + +import win32api +import win32con +import win32ui + +from . import scintillacon + +WM_KICKIDLE = 0x036A + +# Used to indicate that style should use default color +from win32con import CLR_INVALID + +debugging = 0 +if debugging: + # Output must go to another process else the result of + # the printing itself will trigger again trigger a trace. + + import win32trace + import win32traceutil + + def trace(*args): + win32trace.write(" ".join(map(str, args)) + "\n") + +else: + trace = lambda *args: None + + +class Style: + """Represents a single format""" + + def __init__(self, name, format, background=CLR_INVALID): + self.name = name # Name the format representes eg, "String", "Class" + # Default background for each style is only used when there are no + # saved settings (generally on first startup) + self.background = self.default_background = background + if type(format) == type(""): + self.aliased = format + self.format = None + else: + self.format = format + self.aliased = None + self.stylenum = None # Not yet registered. + + def IsBasedOnDefault(self): + return len(self.format) == 5 + + # If the currently extended font defintion matches the + # default format, restore the format to the "simple" format. + def NormalizeAgainstDefault(self, defaultFormat): + if self.IsBasedOnDefault(): + return 0 # No more to do, and not changed. + bIsDefault = ( + self.format[7] == defaultFormat[7] and self.format[2] == defaultFormat[2] + ) + if bIsDefault: + self.ForceAgainstDefault() + return bIsDefault + + def ForceAgainstDefault(self): + self.format = self.format[:5] + + def GetCompleteFormat(self, defaultFormat): + # Get the complete style after applying any relevant defaults. + if len(self.format) == 5: # It is a default one + fmt = self.format + defaultFormat[5:] + else: + fmt = self.format + flags = ( + win32con.CFM_BOLD + | win32con.CFM_CHARSET + | win32con.CFM_COLOR + | win32con.CFM_FACE + | win32con.CFM_ITALIC + | win32con.CFM_SIZE + ) + return (flags,) + fmt[1:] + + +# The Formatter interface +# used primarily when the actual formatting is done by Scintilla! +class FormatterBase: + def __init__(self, scintilla): + self.scintilla = scintilla + self.baseFormatFixed = (-402653169, 0, 200, 0, 0, 0, 49, "Courier New") + self.baseFormatProp = (-402653169, 0, 200, 0, 0, 0, 49, "Arial") + self.bUseFixed = 1 + self.styles = {} # Indexed by name + self.styles_by_id = {} # Indexed by allocated ID. + self.SetStyles() + + def HookFormatter(self, parent=None): + raise NotImplementedError() + + # Used by the IDLE extensions to quickly determine if a character is a string. + def GetStringStyle(self, pos): + try: + style = self.styles_by_id[self.scintilla.SCIGetStyleAt(pos)] + except KeyError: + # A style we dont know about - probably not even a .py file - can't be a string + return None + if style.name in self.string_style_names: + return style + return None + + def RegisterStyle(self, style, stylenum): + assert stylenum is not None, "We must have a style number" + assert style.stylenum is None, "Style has already been registered" + assert stylenum not in self.styles, "We are reusing a style number!" + style.stylenum = stylenum + self.styles[style.name] = style + self.styles_by_id[stylenum] = style + + def SetStyles(self): + raise NotImplementedError() + + def GetSampleText(self): + return "Sample Text for the Format Dialog" + + def GetDefaultFormat(self): + if self.bUseFixed: + return self.baseFormatFixed + return self.baseFormatProp + + # Update the control with the new style format. + def _ReformatStyle(self, style): + ## Selection (background only for now) + ## Passing False for WPARAM to SCI_SETSELBACK is documented as resetting to scintilla default, + ## but does not work - selection background is not visible at all. + ## Default value in SPECIAL_STYLES taken from scintilla source. + if style.name == STYLE_SELECTION: + clr = style.background + self.scintilla.SendScintilla(scintillacon.SCI_SETSELBACK, True, clr) + + ## Can't change font for selection, but could set color + ## However, the font color dropbox has no option for default, and thus would + ## always override syntax coloring + ## clr = style.format[4] + ## self.scintilla.SendScintilla(scintillacon.SCI_SETSELFORE, clr != CLR_INVALID, clr) + return + + assert style.stylenum is not None, "Unregistered style." + # print "Reformat style", style.name, style.stylenum + scintilla = self.scintilla + stylenum = style.stylenum + # Now we have the style number, indirect for the actual style. + if style.aliased is not None: + style = self.styles[style.aliased] + f = style.format + if style.IsBasedOnDefault(): + baseFormat = self.GetDefaultFormat() + else: + baseFormat = f + scintilla.SCIStyleSetFore(stylenum, f[4]) + scintilla.SCIStyleSetFont(stylenum, baseFormat[7], baseFormat[5]) + if f[1] & 1: + scintilla.SCIStyleSetBold(stylenum, 1) + else: + scintilla.SCIStyleSetBold(stylenum, 0) + if f[1] & 2: + scintilla.SCIStyleSetItalic(stylenum, 1) + else: + scintilla.SCIStyleSetItalic(stylenum, 0) + scintilla.SCIStyleSetSize(stylenum, int(baseFormat[2] / 20)) + scintilla.SCIStyleSetEOLFilled(stylenum, 1) # Only needed for unclosed strings. + + ## Default style background to whitespace background if set, + ## otherwise use system window color + bg = style.background + if bg == CLR_INVALID: + bg = self.styles[STYLE_DEFAULT].background + if bg == CLR_INVALID: + bg = win32api.GetSysColor(win32con.COLOR_WINDOW) + scintilla.SCIStyleSetBack(stylenum, bg) + + def GetStyleByNum(self, stylenum): + return self.styles_by_id[stylenum] + + def ApplyFormattingStyles(self, bReload=1): + if bReload: + self.LoadPreferences() + baseFormat = self.GetDefaultFormat() + defaultStyle = Style("default", baseFormat) + defaultStyle.stylenum = scintillacon.STYLE_DEFAULT + self._ReformatStyle(defaultStyle) + for style in list(self.styles.values()): + if style.aliased is None: + style.NormalizeAgainstDefault(baseFormat) + self._ReformatStyle(style) + self.scintilla.InvalidateRect() + + # Some functions for loading and saving preferences. By default + # an INI file (well, MFC maps this to the registry) is used. + def LoadPreferences(self): + self.baseFormatFixed = eval( + self.LoadPreference("Base Format Fixed", str(self.baseFormatFixed)) + ) + self.baseFormatProp = eval( + self.LoadPreference("Base Format Proportional", str(self.baseFormatProp)) + ) + self.bUseFixed = int(self.LoadPreference("Use Fixed", 1)) + + for style in list(self.styles.values()): + new = self.LoadPreference(style.name, str(style.format)) + try: + style.format = eval(new) + except: + print("Error loading style data for", style.name) + # Use "vanilla" background hardcoded in PYTHON_STYLES if no settings in registry + style.background = int( + self.LoadPreference( + style.name + " background", style.default_background + ) + ) + + def LoadPreference(self, name, default): + return win32ui.GetProfileVal("Format", name, default) + + def SavePreferences(self): + self.SavePreference("Base Format Fixed", str(self.baseFormatFixed)) + self.SavePreference("Base Format Proportional", str(self.baseFormatProp)) + self.SavePreference("Use Fixed", self.bUseFixed) + for style in list(self.styles.values()): + if style.aliased is None: + self.SavePreference(style.name, str(style.format)) + bg_name = style.name + " background" + self.SavePreference(bg_name, style.background) + + def SavePreference(self, name, value): + win32ui.WriteProfileVal("Format", name, value) + + +# An abstract formatter +# For all formatters we actually implement here. +# (as opposed to those formatters built in to Scintilla) +class Formatter(FormatterBase): + def __init__(self, scintilla): + self.bCompleteWhileIdle = 0 + self.bHaveIdleHandler = 0 # Dont currently have an idle handle + self.nextstylenum = 0 + FormatterBase.__init__(self, scintilla) + + def HookFormatter(self, parent=None): + if parent is None: + parent = self.scintilla.GetParent() # was GetParentFrame()!? + parent.HookNotify(self.OnStyleNeeded, scintillacon.SCN_STYLENEEDED) + + def OnStyleNeeded(self, std, extra): + notify = self.scintilla.SCIUnpackNotifyMessage(extra) + endStyledChar = self.scintilla.SendScintilla(scintillacon.SCI_GETENDSTYLED) + lineEndStyled = self.scintilla.LineFromChar(endStyledChar) + endStyled = self.scintilla.LineIndex(lineEndStyled) + # print "enPosPaint %d endStyledChar %d lineEndStyled %d endStyled %d" % (endPosPaint, endStyledChar, lineEndStyled, endStyled) + self.Colorize(endStyled, notify.position) + + def ColorSeg(self, start, end, styleName): + end = end + 1 + # assert end-start>=0, "Can't have negative styling" + stylenum = self.styles[styleName].stylenum + while start < end: + self.style_buffer[start] = stylenum + start = start + 1 + # self.scintilla.SCISetStyling(end - start + 1, stylenum) + + def RegisterStyle(self, style, stylenum=None): + if stylenum is None: + stylenum = self.nextstylenum + self.nextstylenum = self.nextstylenum + 1 + FormatterBase.RegisterStyle(self, style, stylenum) + + def ColorizeString(self, str, charStart, styleStart): + raise RuntimeError("You must override this method") + + def Colorize(self, start=0, end=-1): + scintilla = self.scintilla + # scintilla's formatting is all done in terms of utf, so + # we work with utf8 bytes instead of unicode. This magically + # works as any extended chars found in the utf8 don't change + # the semantics. + stringVal = scintilla.GetTextRange(start, end, decode=False) + if start > 0: + stylenum = scintilla.SCIGetStyleAt(start - 1) + styleStart = self.GetStyleByNum(stylenum).name + else: + styleStart = None + # trace("Coloring", start, end, end-start, len(stringVal), styleStart, self.scintilla.SCIGetCharAt(start)) + scintilla.SCIStartStyling(start, 31) + self.style_buffer = array.array("b", (0,) * len(stringVal)) + self.ColorizeString(stringVal, styleStart) + scintilla.SCISetStylingEx(self.style_buffer) + self.style_buffer = None + # trace("After styling, end styled is", self.scintilla.SCIGetEndStyled()) + if ( + self.bCompleteWhileIdle + and not self.bHaveIdleHandler + and end != -1 + and end < scintilla.GetTextLength() + ): + self.bHaveIdleHandler = 1 + win32ui.GetApp().AddIdleHandler(self.DoMoreColoring) + # Kicking idle makes the app seem slower when initially repainting! + + # win32ui.GetMainFrame().PostMessage(WM_KICKIDLE, 0, 0) + + def DoMoreColoring(self, handler, count): + try: + scintilla = self.scintilla + endStyled = scintilla.SCIGetEndStyled() + lineStartStyled = scintilla.LineFromChar(endStyled) + start = scintilla.LineIndex(lineStartStyled) + end = scintilla.LineIndex(lineStartStyled + 1) + textlen = scintilla.GetTextLength() + if end < 0: + end = textlen + + finished = end >= textlen + self.Colorize(start, end) + except (win32ui.error, AttributeError): + # Window may have closed before we finished - no big deal! + finished = 1 + + if finished: + self.bHaveIdleHandler = 0 + win32ui.GetApp().DeleteIdleHandler(handler) + return not finished + + +# A Formatter that knows how to format Python source +from keyword import iskeyword, kwlist + +wordstarts = "_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +wordchars = "._0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +operators = "%^&*()-+=|{}[]:;<>,/?!.~" + +STYLE_DEFAULT = "Whitespace" +STYLE_COMMENT = "Comment" +STYLE_COMMENT_BLOCK = "Comment Blocks" +STYLE_NUMBER = "Number" +STYLE_STRING = "String" +STYLE_SQSTRING = "SQ String" +STYLE_TQSSTRING = "TQS String" +STYLE_TQDSTRING = "TQD String" +STYLE_KEYWORD = "Keyword" +STYLE_CLASS = "Class" +STYLE_METHOD = "Method" +STYLE_OPERATOR = "Operator" +STYLE_IDENTIFIER = "Identifier" +STYLE_BRACE = "Brace/Paren - matching" +STYLE_BRACEBAD = "Brace/Paren - unmatched" +STYLE_STRINGEOL = "String with no terminator" +STYLE_LINENUMBER = "Line numbers" +STYLE_INDENTGUIDE = "Indent guide" +STYLE_SELECTION = "Selection" + +STRING_STYLES = [ + STYLE_STRING, + STYLE_SQSTRING, + STYLE_TQSSTRING, + STYLE_TQDSTRING, + STYLE_STRINGEOL, +] + +# These styles can have any ID - they are not special to scintilla itself. +# However, if we use the built-in lexer, then we must use its style numbers +# so in that case, they _are_ special. +# (name, format, background, scintilla id) +PYTHON_STYLES = [ + (STYLE_DEFAULT, (0, 0, 200, 0, 0x808080), CLR_INVALID, scintillacon.SCE_P_DEFAULT), + ( + STYLE_COMMENT, + (0, 2, 200, 0, 0x008000), + CLR_INVALID, + scintillacon.SCE_P_COMMENTLINE, + ), + ( + STYLE_COMMENT_BLOCK, + (0, 2, 200, 0, 0x808080), + CLR_INVALID, + scintillacon.SCE_P_COMMENTBLOCK, + ), + (STYLE_NUMBER, (0, 0, 200, 0, 0x808000), CLR_INVALID, scintillacon.SCE_P_NUMBER), + (STYLE_STRING, (0, 0, 200, 0, 0x008080), CLR_INVALID, scintillacon.SCE_P_STRING), + (STYLE_SQSTRING, STYLE_STRING, CLR_INVALID, scintillacon.SCE_P_CHARACTER), + (STYLE_TQSSTRING, STYLE_STRING, CLR_INVALID, scintillacon.SCE_P_TRIPLE), + (STYLE_TQDSTRING, STYLE_STRING, CLR_INVALID, scintillacon.SCE_P_TRIPLEDOUBLE), + (STYLE_STRINGEOL, (0, 0, 200, 0, 0x000000), 0x008080, scintillacon.SCE_P_STRINGEOL), + (STYLE_KEYWORD, (0, 1, 200, 0, 0x800000), CLR_INVALID, scintillacon.SCE_P_WORD), + (STYLE_CLASS, (0, 1, 200, 0, 0xFF0000), CLR_INVALID, scintillacon.SCE_P_CLASSNAME), + (STYLE_METHOD, (0, 1, 200, 0, 0x808000), CLR_INVALID, scintillacon.SCE_P_DEFNAME), + ( + STYLE_OPERATOR, + (0, 0, 200, 0, 0x000000), + CLR_INVALID, + scintillacon.SCE_P_OPERATOR, + ), + ( + STYLE_IDENTIFIER, + (0, 0, 200, 0, 0x000000), + CLR_INVALID, + scintillacon.SCE_P_IDENTIFIER, + ), +] + +# These styles _always_ have this specific style number, regardless of +# internal or external formatter. +SPECIAL_STYLES = [ + (STYLE_BRACE, (0, 0, 200, 0, 0x000000), 0xFFFF80, scintillacon.STYLE_BRACELIGHT), + (STYLE_BRACEBAD, (0, 0, 200, 0, 0x000000), 0x8EA5F2, scintillacon.STYLE_BRACEBAD), + ( + STYLE_LINENUMBER, + (0, 0, 200, 0, 0x000000), + win32api.GetSysColor(win32con.COLOR_3DFACE), + scintillacon.STYLE_LINENUMBER, + ), + ( + STYLE_INDENTGUIDE, + (0, 0, 200, 0, 0x000000), + CLR_INVALID, + scintillacon.STYLE_INDENTGUIDE, + ), + ## Not actually a style; requires special handling to send appropriate messages to scintilla + ( + STYLE_SELECTION, + (0, 0, 200, 0, CLR_INVALID), + win32api.RGB(0xC0, 0xC0, 0xC0), + 999999, + ), +] + +PythonSampleCode = """\ +# Some Python +class Sample(Super): + def Fn(self): +\tself.v = 1024 +dest = 'dest.html' +x = func(a + 1)|) +s = "I forget... +## A large +## comment block""" + + +class PythonSourceFormatter(Formatter): + string_style_names = STRING_STYLES + + def GetSampleText(self): + return PythonSampleCode + + def LoadStyles(self): + pass + + def SetStyles(self): + for name, format, bg, ignore in PYTHON_STYLES: + self.RegisterStyle(Style(name, format, bg)) + for name, format, bg, sc_id in SPECIAL_STYLES: + self.RegisterStyle(Style(name, format, bg), sc_id) + + def ClassifyWord(self, cdoc, start, end, prevWord): + word = cdoc[start : end + 1].decode("latin-1") + attr = STYLE_IDENTIFIER + if prevWord == "class": + attr = STYLE_CLASS + elif prevWord == "def": + attr = STYLE_METHOD + elif word[0] in string.digits: + attr = STYLE_NUMBER + elif iskeyword(word): + attr = STYLE_KEYWORD + self.ColorSeg(start, end, attr) + return word + + def ColorizeString(self, str, styleStart): + if styleStart is None: + styleStart = STYLE_DEFAULT + return self.ColorizePythonCode(str, 0, styleStart) + + def ColorizePythonCode(self, cdoc, charStart, styleStart): + # Straight translation of C++, should do better + lengthDoc = len(cdoc) + if lengthDoc <= charStart: + return + prevWord = "" + state = styleStart + chPrev = chPrev2 = chPrev3 = " " + chNext2 = chNext = cdoc[charStart : charStart + 1].decode("latin-1") + startSeg = i = charStart + while i < lengthDoc: + ch = chNext + chNext = " " + if i + 1 < lengthDoc: + chNext = cdoc[i + 1 : i + 2].decode("latin-1") + chNext2 = " " + if i + 2 < lengthDoc: + chNext2 = cdoc[i + 2 : i + 3].decode("latin-1") + if state == STYLE_DEFAULT: + if ch in wordstarts: + self.ColorSeg(startSeg, i - 1, STYLE_DEFAULT) + state = STYLE_KEYWORD + startSeg = i + elif ch == "#": + self.ColorSeg(startSeg, i - 1, STYLE_DEFAULT) + if chNext == "#": + state = STYLE_COMMENT_BLOCK + else: + state = STYLE_COMMENT + startSeg = i + elif ch == '"': + self.ColorSeg(startSeg, i - 1, STYLE_DEFAULT) + startSeg = i + state = STYLE_COMMENT + if chNext == '"' and chNext2 == '"': + i = i + 2 + state = STYLE_TQDSTRING + ch = " " + chPrev = " " + chNext = " " + if i + 1 < lengthDoc: + chNext = cdoc[i + 1] + else: + state = STYLE_STRING + elif ch == "'": + self.ColorSeg(startSeg, i - 1, STYLE_DEFAULT) + startSeg = i + state = STYLE_COMMENT + if chNext == "'" and chNext2 == "'": + i = i + 2 + state = STYLE_TQSSTRING + ch = " " + chPrev = " " + chNext = " " + if i + 1 < lengthDoc: + chNext = cdoc[i + 1] + else: + state = STYLE_SQSTRING + elif ch in operators: + self.ColorSeg(startSeg, i - 1, STYLE_DEFAULT) + self.ColorSeg(i, i, STYLE_OPERATOR) + startSeg = i + 1 + elif state == STYLE_KEYWORD: + if ch not in wordchars: + prevWord = self.ClassifyWord(cdoc, startSeg, i - 1, prevWord) + state = STYLE_DEFAULT + startSeg = i + if ch == "#": + if chNext == "#": + state = STYLE_COMMENT_BLOCK + else: + state = STYLE_COMMENT + elif ch == '"': + if chNext == '"' and chNext2 == '"': + i = i + 2 + state = STYLE_TQDSTRING + ch = " " + chPrev = " " + chNext = " " + if i + 1 < lengthDoc: + chNext = cdoc[i + 1] + else: + state = STYLE_STRING + elif ch == "'": + if chNext == "'" and chNext2 == "'": + i = i + 2 + state = STYLE_TQSSTRING + ch = " " + chPrev = " " + chNext = " " + if i + 1 < lengthDoc: + chNext = cdoc[i + 1] + else: + state = STYLE_SQSTRING + elif ch in operators: + self.ColorSeg(startSeg, i, STYLE_OPERATOR) + startSeg = i + 1 + elif state == STYLE_COMMENT or state == STYLE_COMMENT_BLOCK: + if ch == "\r" or ch == "\n": + self.ColorSeg(startSeg, i - 1, state) + state = STYLE_DEFAULT + startSeg = i + elif state == STYLE_STRING: + if ch == "\\": + if chNext == '"' or chNext == "'" or chNext == "\\": + i = i + 1 + ch = chNext + chNext = " " + if i + 1 < lengthDoc: + chNext = cdoc[i + 1] + elif ch == '"': + self.ColorSeg(startSeg, i, STYLE_STRING) + state = STYLE_DEFAULT + startSeg = i + 1 + elif state == STYLE_SQSTRING: + if ch == "\\": + if chNext == '"' or chNext == "'" or chNext == "\\": + i = i + 1 + ch = chNext + chNext = " " + if i + 1 < lengthDoc: + chNext = cdoc[i + 1] + elif ch == "'": + self.ColorSeg(startSeg, i, STYLE_SQSTRING) + state = STYLE_DEFAULT + startSeg = i + 1 + elif state == STYLE_TQSSTRING: + if ch == "'" and chPrev == "'" and chPrev2 == "'" and chPrev3 != "\\": + self.ColorSeg(startSeg, i, STYLE_TQSSTRING) + state = STYLE_DEFAULT + startSeg = i + 1 + elif ( + state == STYLE_TQDSTRING + and ch == '"' + and chPrev == '"' + and chPrev2 == '"' + and chPrev3 != "\\" + ): + self.ColorSeg(startSeg, i, STYLE_TQDSTRING) + state = STYLE_DEFAULT + startSeg = i + 1 + chPrev3 = chPrev2 + chPrev2 = chPrev + chPrev = ch + i = i + 1 + if startSeg < lengthDoc: + if state == STYLE_KEYWORD: + self.ClassifyWord(cdoc, startSeg, lengthDoc - 1, prevWord) + else: + self.ColorSeg(startSeg, lengthDoc - 1, state) + + +# These taken from the SciTE properties file. +source_formatter_extensions = [ + (".py .pys .pyw".split(), scintillacon.SCLEX_PYTHON), + (".html .htm .asp .shtml".split(), scintillacon.SCLEX_HTML), + ( + "c .cc .cpp .cxx .h .hh .hpp .hxx .idl .odl .php3 .phtml .inc .js".split(), + scintillacon.SCLEX_CPP, + ), + (".vbs .frm .ctl .cls".split(), scintillacon.SCLEX_VB), + (".pl .pm .cgi .pod".split(), scintillacon.SCLEX_PERL), + (".sql .spec .body .sps .spb .sf .sp".split(), scintillacon.SCLEX_SQL), + (".tex .sty".split(), scintillacon.SCLEX_LATEX), + (".xml .xul".split(), scintillacon.SCLEX_XML), + (".err".split(), scintillacon.SCLEX_ERRORLIST), + (".mak".split(), scintillacon.SCLEX_MAKEFILE), + (".bat .cmd".split(), scintillacon.SCLEX_BATCH), +] + + +class BuiltinSourceFormatter(FormatterBase): + # A class that represents a formatter built-in to Scintilla + def __init__(self, scintilla, ext): + self.ext = ext + FormatterBase.__init__(self, scintilla) + + def Colorize(self, start=0, end=-1): + self.scintilla.SendScintilla(scintillacon.SCI_COLOURISE, start, end) + + def RegisterStyle(self, style, stylenum=None): + assert style.stylenum is None, "Style has already been registered" + if stylenum is None: + stylenum = self.nextstylenum + self.nextstylenum = self.nextstylenum + 1 + assert self.styles.get(stylenum) is None, "We are reusing a style number!" + style.stylenum = stylenum + self.styles[style.name] = style + self.styles_by_id[stylenum] = style + + def HookFormatter(self, parent=None): + sc = self.scintilla + for exts, formatter in source_formatter_extensions: + if self.ext in exts: + formatter_use = formatter + break + else: + formatter_use = scintillacon.SCLEX_PYTHON + sc.SendScintilla(scintillacon.SCI_SETLEXER, formatter_use) + keywords = " ".join(kwlist) + sc.SCISetKeywords(keywords) + + +class BuiltinPythonSourceFormatter(BuiltinSourceFormatter): + sci_lexer_name = scintillacon.SCLEX_PYTHON + string_style_names = STRING_STYLES + + def __init__(self, sc, ext=".py"): + BuiltinSourceFormatter.__init__(self, sc, ext) + + def SetStyles(self): + for name, format, bg, sc_id in PYTHON_STYLES: + self.RegisterStyle(Style(name, format, bg), sc_id) + for name, format, bg, sc_id in SPECIAL_STYLES: + self.RegisterStyle(Style(name, format, bg), sc_id) + + def GetSampleText(self): + return PythonSampleCode diff --git a/myenv/Lib/site-packages/pythonwin/pywin/scintilla/keycodes.py b/myenv/Lib/site-packages/pythonwin/pywin/scintilla/keycodes.py new file mode 100644 index 000000000..badcef3e7 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/scintilla/keycodes.py @@ -0,0 +1,190 @@ +import win32api +import win32con +import win32ui + +MAPVK_VK_TO_CHAR = 2 + +key_name_to_vk = {} +key_code_to_name = {} + +_better_names = { + "escape": "esc", + "return": "enter", + "back": "pgup", + "next": "pgdn", +} + + +def _fillvkmap(): + # Pull the VK_names from win32con + names = [entry for entry in win32con.__dict__ if entry.startswith("VK_")] + for name in names: + code = getattr(win32con, name) + n = name[3:].lower() + key_name_to_vk[n] = code + if n in _better_names: + n = _better_names[n] + key_name_to_vk[n] = code + key_code_to_name[code] = n + + +_fillvkmap() + + +def get_vk(chardesc): + if len(chardesc) == 1: + # it is a character. + info = win32api.VkKeyScan(chardesc) + if info == -1: + # Note: returning None, None causes an error when keyboard layout is non-English, see the report below + # https://stackoverflow.com/questions/45138084/pythonwin-occasionally-gives-an-error-on-opening + return 0, 0 + vk = win32api.LOBYTE(info) + state = win32api.HIBYTE(info) + modifiers = 0 + if state & 0x1: + modifiers |= win32con.SHIFT_PRESSED + if state & 0x2: + modifiers |= win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED + if state & 0x4: + modifiers |= win32con.LEFT_ALT_PRESSED | win32con.RIGHT_ALT_PRESSED + return vk, modifiers + # must be a 'key name' + return key_name_to_vk.get(chardesc.lower()), 0 + + +modifiers = { + "alt": win32con.LEFT_ALT_PRESSED | win32con.RIGHT_ALT_PRESSED, + "lalt": win32con.LEFT_ALT_PRESSED, + "ralt": win32con.RIGHT_ALT_PRESSED, + "ctrl": win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED, + "ctl": win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED, + "control": win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED, + "lctrl": win32con.LEFT_CTRL_PRESSED, + "lctl": win32con.LEFT_CTRL_PRESSED, + "rctrl": win32con.RIGHT_CTRL_PRESSED, + "rctl": win32con.RIGHT_CTRL_PRESSED, + "shift": win32con.SHIFT_PRESSED, + "key": 0, # ignore key tag. +} + + +def parse_key_name(name): + name = name + "-" # Add a sentinal + start = pos = 0 + max = len(name) + toks = [] + while pos < max: + if name[pos] in "+-": + tok = name[start:pos] + # use the ascii lower() version of tok, so ascii chars require + # an explicit shift modifier - ie 'Ctrl+G' should be treated as + # 'ctrl+g' - 'ctrl+shift+g' would be needed if desired. + # This is mainly to avoid changing all the old keystroke defs + toks.append(tok.lower()) + pos += 1 # skip the sep + start = pos + pos += 1 + flags = 0 + # do the modifiers + for tok in toks[:-1]: + mod = modifiers.get(tok.lower()) + if mod is not None: + flags |= mod + # the key name + vk, this_flags = get_vk(toks[-1]) + return vk, flags | this_flags + + +_checks = [ + [ # Shift + ("Shift", win32con.SHIFT_PRESSED), + ], + [ # Ctrl key + ("Ctrl", win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED), + ("LCtrl", win32con.LEFT_CTRL_PRESSED), + ("RCtrl", win32con.RIGHT_CTRL_PRESSED), + ], + [ # Alt key + ("Alt", win32con.LEFT_ALT_PRESSED | win32con.RIGHT_ALT_PRESSED), + ("LAlt", win32con.LEFT_ALT_PRESSED), + ("RAlt", win32con.RIGHT_ALT_PRESSED), + ], +] + + +def make_key_name(vk, flags): + # Check alt keys. + flags_done = 0 + parts = [] + for moddata in _checks: + for name, checkflag in moddata: + if flags & checkflag: + parts.append(name) + flags_done = flags_done & checkflag + break + if flags_done & flags: + parts.append(hex(flags & ~flags_done)) + # Now the key name. + if vk is None: + parts.append("") + else: + try: + parts.append(key_code_to_name[vk]) + except KeyError: + # Not in our virtual key map - ask Windows what character this + # key corresponds to. + scancode = win32api.MapVirtualKey(vk, MAPVK_VK_TO_CHAR) + parts.append(chr(scancode)) + sep = "+" + if sep in parts: + sep = "-" + return sep.join([p.capitalize() for p in parts]) + + +def _psc(char): + sc, mods = get_vk(char) + print("Char %s -> %d -> %s" % (repr(char), sc, key_code_to_name.get(sc))) + + +def test1(): + for ch in """aA0/?[{}];:'"`~_-+=\\|,<.>/?""": + _psc(ch) + for code in ["Home", "End", "Left", "Right", "Up", "Down", "Menu", "Next"]: + _psc(code) + + +def _pkn(n): + vk, flags = parse_key_name(n) + print("%s -> %s,%s -> %s" % (n, vk, flags, make_key_name(vk, flags))) + + +def test2(): + _pkn("ctrl+alt-shift+x") + _pkn("ctrl-home") + _pkn("Shift-+") + _pkn("Shift--") + _pkn("Shift+-") + _pkn("Shift++") + _pkn("LShift-+") + _pkn("ctl+home") + _pkn("ctl+enter") + _pkn("alt+return") + _pkn("Alt+/") + _pkn("Alt+BadKeyName") + _pkn("A") # an ascii char - should be seen as 'a' + _pkn("a") + _pkn("Shift-A") + _pkn("Shift-a") + _pkn("a") + _pkn("(") + _pkn("Ctrl+(") + _pkn("Ctrl+Shift-8") + _pkn("Ctrl+*") + _pkn("{") + _pkn("!") + _pkn(".") + + +if __name__ == "__main__": + test2() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/scintilla/scintillacon.py b/myenv/Lib/site-packages/pythonwin/pywin/scintilla/scintillacon.py new file mode 100644 index 000000000..e7657dc77 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/scintilla/scintillacon.py @@ -0,0 +1,2001 @@ +# Generated by h2py from Include\scintilla.h + + +# Included from BaseTsd.h +def HandleToUlong(h): + return HandleToULong(h) + + +def UlongToHandle(ul): + return ULongToHandle(ul) + + +def UlongToPtr(ul): + return ULongToPtr(ul) + + +def UintToPtr(ui): + return UIntToPtr(ui) + + +INVALID_POSITION = -1 +SCI_START = 2000 +SCI_OPTIONAL_START = 3000 +SCI_LEXER_START = 4000 +SCI_ADDTEXT = 2001 +SCI_ADDSTYLEDTEXT = 2002 +SCI_INSERTTEXT = 2003 +SCI_CLEARALL = 2004 +SCI_CLEARDOCUMENTSTYLE = 2005 +SCI_GETLENGTH = 2006 +SCI_GETCHARAT = 2007 +SCI_GETCURRENTPOS = 2008 +SCI_GETANCHOR = 2009 +SCI_GETSTYLEAT = 2010 +SCI_REDO = 2011 +SCI_SETUNDOCOLLECTION = 2012 +SCI_SELECTALL = 2013 +SCI_SETSAVEPOINT = 2014 +SCI_GETSTYLEDTEXT = 2015 +SCI_CANREDO = 2016 +SCI_MARKERLINEFROMHANDLE = 2017 +SCI_MARKERDELETEHANDLE = 2018 +SCI_GETUNDOCOLLECTION = 2019 +SCWS_INVISIBLE = 0 +SCWS_VISIBLEALWAYS = 1 +SCWS_VISIBLEAFTERINDENT = 2 +SCI_GETVIEWWS = 2020 +SCI_SETVIEWWS = 2021 +SCI_POSITIONFROMPOINT = 2022 +SCI_POSITIONFROMPOINTCLOSE = 2023 +SCI_GOTOLINE = 2024 +SCI_GOTOPOS = 2025 +SCI_SETANCHOR = 2026 +SCI_GETCURLINE = 2027 +SCI_GETENDSTYLED = 2028 +SC_EOL_CRLF = 0 +SC_EOL_CR = 1 +SC_EOL_LF = 2 +SCI_CONVERTEOLS = 2029 +SCI_GETEOLMODE = 2030 +SCI_SETEOLMODE = 2031 +SCI_STARTSTYLING = 2032 +SCI_SETSTYLING = 2033 +SCI_GETBUFFEREDDRAW = 2034 +SCI_SETBUFFEREDDRAW = 2035 +SCI_SETTABWIDTH = 2036 +SCI_GETTABWIDTH = 2121 +SC_CP_UTF8 = 65001 +SC_CP_DBCS = 1 +SCI_SETCODEPAGE = 2037 +SCI_SETUSEPALETTE = 2039 +MARKER_MAX = 31 +SC_MARK_CIRCLE = 0 +SC_MARK_ROUNDRECT = 1 +SC_MARK_ARROW = 2 +SC_MARK_SMALLRECT = 3 +SC_MARK_SHORTARROW = 4 +SC_MARK_EMPTY = 5 +SC_MARK_ARROWDOWN = 6 +SC_MARK_MINUS = 7 +SC_MARK_PLUS = 8 +SC_MARK_VLINE = 9 +SC_MARK_LCORNER = 10 +SC_MARK_TCORNER = 11 +SC_MARK_BOXPLUS = 12 +SC_MARK_BOXPLUSCONNECTED = 13 +SC_MARK_BOXMINUS = 14 +SC_MARK_BOXMINUSCONNECTED = 15 +SC_MARK_LCORNERCURVE = 16 +SC_MARK_TCORNERCURVE = 17 +SC_MARK_CIRCLEPLUS = 18 +SC_MARK_CIRCLEPLUSCONNECTED = 19 +SC_MARK_CIRCLEMINUS = 20 +SC_MARK_CIRCLEMINUSCONNECTED = 21 +SC_MARK_BACKGROUND = 22 +SC_MARK_DOTDOTDOT = 23 +SC_MARK_ARROWS = 24 +SC_MARK_PIXMAP = 25 +SC_MARK_FULLRECT = 26 +SC_MARK_LEFTRECT = 27 +SC_MARK_CHARACTER = 10000 +SC_MARKNUM_FOLDEREND = 25 +SC_MARKNUM_FOLDEROPENMID = 26 +SC_MARKNUM_FOLDERMIDTAIL = 27 +SC_MARKNUM_FOLDERTAIL = 28 +SC_MARKNUM_FOLDERSUB = 29 +SC_MARKNUM_FOLDER = 30 +SC_MARKNUM_FOLDEROPEN = 31 +SC_MASK_FOLDERS = -33554432 +SCI_MARKERDEFINE = 2040 +SCI_MARKERSETFORE = 2041 +SCI_MARKERSETBACK = 2042 +SCI_MARKERADD = 2043 +SCI_MARKERDELETE = 2044 +SCI_MARKERDELETEALL = 2045 +SCI_MARKERGET = 2046 +SCI_MARKERNEXT = 2047 +SCI_MARKERPREVIOUS = 2048 +SCI_MARKERDEFINEPIXMAP = 2049 +SCI_MARKERADDSET = 2466 +SCI_MARKERSETALPHA = 2476 +SC_MARGIN_SYMBOL = 0 +SC_MARGIN_NUMBER = 1 +SC_MARGIN_BACK = 2 +SC_MARGIN_FORE = 3 +SCI_SETMARGINTYPEN = 2240 +SCI_GETMARGINTYPEN = 2241 +SCI_SETMARGINWIDTHN = 2242 +SCI_GETMARGINWIDTHN = 2243 +SCI_SETMARGINMASKN = 2244 +SCI_GETMARGINMASKN = 2245 +SCI_SETMARGINSENSITIVEN = 2246 +SCI_GETMARGINSENSITIVEN = 2247 +STYLE_DEFAULT = 32 +STYLE_LINENUMBER = 33 +STYLE_BRACELIGHT = 34 +STYLE_BRACEBAD = 35 +STYLE_CONTROLCHAR = 36 +STYLE_INDENTGUIDE = 37 +STYLE_CALLTIP = 38 +STYLE_LASTPREDEFINED = 39 +STYLE_MAX = 255 +SC_CHARSET_ANSI = 0 +SC_CHARSET_DEFAULT = 1 +SC_CHARSET_BALTIC = 186 +SC_CHARSET_CHINESEBIG5 = 136 +SC_CHARSET_EASTEUROPE = 238 +SC_CHARSET_GB2312 = 134 +SC_CHARSET_GREEK = 161 +SC_CHARSET_HANGUL = 129 +SC_CHARSET_MAC = 77 +SC_CHARSET_OEM = 255 +SC_CHARSET_RUSSIAN = 204 +SC_CHARSET_CYRILLIC = 1251 +SC_CHARSET_SHIFTJIS = 128 +SC_CHARSET_SYMBOL = 2 +SC_CHARSET_TURKISH = 162 +SC_CHARSET_JOHAB = 130 +SC_CHARSET_HEBREW = 177 +SC_CHARSET_ARABIC = 178 +SC_CHARSET_VIETNAMESE = 163 +SC_CHARSET_THAI = 222 +SC_CHARSET_8859_15 = 1000 +SCI_STYLECLEARALL = 2050 +SCI_STYLESETFORE = 2051 +SCI_STYLESETBACK = 2052 +SCI_STYLESETBOLD = 2053 +SCI_STYLESETITALIC = 2054 +SCI_STYLESETSIZE = 2055 +SCI_STYLESETFONT = 2056 +SCI_STYLESETEOLFILLED = 2057 +SCI_STYLERESETDEFAULT = 2058 +SCI_STYLESETUNDERLINE = 2059 +SC_CASE_MIXED = 0 +SC_CASE_UPPER = 1 +SC_CASE_LOWER = 2 +SCI_STYLEGETFORE = 2481 +SCI_STYLEGETBACK = 2482 +SCI_STYLEGETBOLD = 2483 +SCI_STYLEGETITALIC = 2484 +SCI_STYLEGETSIZE = 2485 +SCI_STYLEGETFONT = 2486 +SCI_STYLEGETEOLFILLED = 2487 +SCI_STYLEGETUNDERLINE = 2488 +SCI_STYLEGETCASE = 2489 +SCI_STYLEGETCHARACTERSET = 2490 +SCI_STYLEGETVISIBLE = 2491 +SCI_STYLEGETCHANGEABLE = 2492 +SCI_STYLEGETHOTSPOT = 2493 +SCI_STYLESETCASE = 2060 +SCI_STYLESETCHARACTERSET = 2066 +SCI_STYLESETHOTSPOT = 2409 +SCI_SETSELFORE = 2067 +SCI_SETSELBACK = 2068 +SCI_GETSELALPHA = 2477 +SCI_SETSELALPHA = 2478 +SCI_GETSELEOLFILLED = 2479 +SCI_SETSELEOLFILLED = 2480 +SCI_SETCARETFORE = 2069 +SCI_ASSIGNCMDKEY = 2070 +SCI_CLEARCMDKEY = 2071 +SCI_CLEARALLCMDKEYS = 2072 +SCI_SETSTYLINGEX = 2073 +SCI_STYLESETVISIBLE = 2074 +SCI_GETCARETPERIOD = 2075 +SCI_SETCARETPERIOD = 2076 +SCI_SETWORDCHARS = 2077 +SCI_BEGINUNDOACTION = 2078 +SCI_ENDUNDOACTION = 2079 +INDIC_PLAIN = 0 +INDIC_SQUIGGLE = 1 +INDIC_TT = 2 +INDIC_DIAGONAL = 3 +INDIC_STRIKE = 4 +INDIC_HIDDEN = 5 +INDIC_BOX = 6 +INDIC_ROUNDBOX = 7 +INDIC_MAX = 31 +INDIC_CONTAINER = 8 +INDIC0_MASK = 0x20 +INDIC1_MASK = 0x40 +INDIC2_MASK = 0x80 +INDICS_MASK = 0xE0 +SCI_INDICSETSTYLE = 2080 +SCI_INDICGETSTYLE = 2081 +SCI_INDICSETFORE = 2082 +SCI_INDICGETFORE = 2083 +SCI_INDICSETUNDER = 2510 +SCI_INDICGETUNDER = 2511 +SCI_SETWHITESPACEFORE = 2084 +SCI_SETWHITESPACEBACK = 2085 +SCI_SETSTYLEBITS = 2090 +SCI_GETSTYLEBITS = 2091 +SCI_SETLINESTATE = 2092 +SCI_GETLINESTATE = 2093 +SCI_GETMAXLINESTATE = 2094 +SCI_GETCARETLINEVISIBLE = 2095 +SCI_SETCARETLINEVISIBLE = 2096 +SCI_GETCARETLINEBACK = 2097 +SCI_SETCARETLINEBACK = 2098 +SCI_STYLESETCHANGEABLE = 2099 +SCI_AUTOCSHOW = 2100 +SCI_AUTOCCANCEL = 2101 +SCI_AUTOCACTIVE = 2102 +SCI_AUTOCPOSSTART = 2103 +SCI_AUTOCCOMPLETE = 2104 +SCI_AUTOCSTOPS = 2105 +SCI_AUTOCSETSEPARATOR = 2106 +SCI_AUTOCGETSEPARATOR = 2107 +SCI_AUTOCSELECT = 2108 +SCI_AUTOCSETCANCELATSTART = 2110 +SCI_AUTOCGETCANCELATSTART = 2111 +SCI_AUTOCSETFILLUPS = 2112 +SCI_AUTOCSETCHOOSESINGLE = 2113 +SCI_AUTOCGETCHOOSESINGLE = 2114 +SCI_AUTOCSETIGNORECASE = 2115 +SCI_AUTOCGETIGNORECASE = 2116 +SCI_USERLISTSHOW = 2117 +SCI_AUTOCSETAUTOHIDE = 2118 +SCI_AUTOCGETAUTOHIDE = 2119 +SCI_AUTOCSETDROPRESTOFWORD = 2270 +SCI_AUTOCGETDROPRESTOFWORD = 2271 +SCI_REGISTERIMAGE = 2405 +SCI_CLEARREGISTEREDIMAGES = 2408 +SCI_AUTOCGETTYPESEPARATOR = 2285 +SCI_AUTOCSETTYPESEPARATOR = 2286 +SCI_AUTOCSETMAXWIDTH = 2208 +SCI_AUTOCGETMAXWIDTH = 2209 +SCI_AUTOCSETMAXHEIGHT = 2210 +SCI_AUTOCGETMAXHEIGHT = 2211 +SCI_SETINDENT = 2122 +SCI_GETINDENT = 2123 +SCI_SETUSETABS = 2124 +SCI_GETUSETABS = 2125 +SCI_SETLINEINDENTATION = 2126 +SCI_GETLINEINDENTATION = 2127 +SCI_GETLINEINDENTPOSITION = 2128 +SCI_GETCOLUMN = 2129 +SCI_SETHSCROLLBAR = 2130 +SCI_GETHSCROLLBAR = 2131 +SC_IV_NONE = 0 +SC_IV_REAL = 1 +SC_IV_LOOKFORWARD = 2 +SC_IV_LOOKBOTH = 3 +SCI_SETINDENTATIONGUIDES = 2132 +SCI_GETINDENTATIONGUIDES = 2133 +SCI_SETHIGHLIGHTGUIDE = 2134 +SCI_GETHIGHLIGHTGUIDE = 2135 +SCI_GETLINEENDPOSITION = 2136 +SCI_GETCODEPAGE = 2137 +SCI_GETCARETFORE = 2138 +SCI_GETUSEPALETTE = 2139 +SCI_GETREADONLY = 2140 +SCI_SETCURRENTPOS = 2141 +SCI_SETSELECTIONSTART = 2142 +SCI_GETSELECTIONSTART = 2143 +SCI_SETSELECTIONEND = 2144 +SCI_GETSELECTIONEND = 2145 +SCI_SETPRINTMAGNIFICATION = 2146 +SCI_GETPRINTMAGNIFICATION = 2147 +SC_PRINT_NORMAL = 0 +SC_PRINT_INVERTLIGHT = 1 +SC_PRINT_BLACKONWHITE = 2 +SC_PRINT_COLOURONWHITE = 3 +SC_PRINT_COLOURONWHITEDEFAULTBG = 4 +SCI_SETPRINTCOLOURMODE = 2148 +SCI_GETPRINTCOLOURMODE = 2149 +SCFIND_WHOLEWORD = 2 +SCFIND_MATCHCASE = 4 +SCFIND_WORDSTART = 0x00100000 +SCFIND_REGEXP = 0x00200000 +SCFIND_POSIX = 0x00400000 +SCI_FINDTEXT = 2150 +SCI_FORMATRANGE = 2151 +SCI_GETFIRSTVISIBLELINE = 2152 +SCI_GETLINE = 2153 +SCI_GETLINECOUNT = 2154 +SCI_SETMARGINLEFT = 2155 +SCI_GETMARGINLEFT = 2156 +SCI_SETMARGINRIGHT = 2157 +SCI_GETMARGINRIGHT = 2158 +SCI_GETMODIFY = 2159 +SCI_SETSEL = 2160 +SCI_GETSELTEXT = 2161 +SCI_GETTEXTRANGE = 2162 +SCI_HIDESELECTION = 2163 +SCI_POINTXFROMPOSITION = 2164 +SCI_POINTYFROMPOSITION = 2165 +SCI_LINEFROMPOSITION = 2166 +SCI_POSITIONFROMLINE = 2167 +SCI_LINESCROLL = 2168 +SCI_SCROLLCARET = 2169 +SCI_REPLACESEL = 2170 +SCI_SETREADONLY = 2171 +SCI_NULL = 2172 +SCI_CANPASTE = 2173 +SCI_CANUNDO = 2174 +SCI_EMPTYUNDOBUFFER = 2175 +SCI_UNDO = 2176 +SCI_CUT = 2177 +SCI_COPY = 2178 +SCI_PASTE = 2179 +SCI_CLEAR = 2180 +SCI_SETTEXT = 2181 +SCI_GETTEXT = 2182 +SCI_GETTEXTLENGTH = 2183 +SCI_GETDIRECTFUNCTION = 2184 +SCI_GETDIRECTPOINTER = 2185 +SCI_SETOVERTYPE = 2186 +SCI_GETOVERTYPE = 2187 +SCI_SETCARETWIDTH = 2188 +SCI_GETCARETWIDTH = 2189 +SCI_SETTARGETSTART = 2190 +SCI_GETTARGETSTART = 2191 +SCI_SETTARGETEND = 2192 +SCI_GETTARGETEND = 2193 +SCI_REPLACETARGET = 2194 +SCI_REPLACETARGETRE = 2195 +SCI_SEARCHINTARGET = 2197 +SCI_SETSEARCHFLAGS = 2198 +SCI_GETSEARCHFLAGS = 2199 +SCI_CALLTIPSHOW = 2200 +SCI_CALLTIPCANCEL = 2201 +SCI_CALLTIPACTIVE = 2202 +SCI_CALLTIPPOSSTART = 2203 +SCI_CALLTIPSETHLT = 2204 +SCI_CALLTIPSETBACK = 2205 +SCI_CALLTIPSETFORE = 2206 +SCI_CALLTIPSETFOREHLT = 2207 +SCI_CALLTIPUSESTYLE = 2212 +SCI_VISIBLEFROMDOCLINE = 2220 +SCI_DOCLINEFROMVISIBLE = 2221 +SCI_WRAPCOUNT = 2235 +SC_FOLDLEVELBASE = 0x400 +SC_FOLDLEVELWHITEFLAG = 0x1000 +SC_FOLDLEVELHEADERFLAG = 0x2000 +SC_FOLDLEVELBOXHEADERFLAG = 0x4000 +SC_FOLDLEVELBOXFOOTERFLAG = 0x8000 +SC_FOLDLEVELCONTRACTED = 0x10000 +SC_FOLDLEVELUNINDENT = 0x20000 +SC_FOLDLEVELNUMBERMASK = 0x0FFF +SCI_SETFOLDLEVEL = 2222 +SCI_GETFOLDLEVEL = 2223 +SCI_GETLASTCHILD = 2224 +SCI_GETFOLDPARENT = 2225 +SCI_SHOWLINES = 2226 +SCI_HIDELINES = 2227 +SCI_GETLINEVISIBLE = 2228 +SCI_SETFOLDEXPANDED = 2229 +SCI_GETFOLDEXPANDED = 2230 +SCI_TOGGLEFOLD = 2231 +SCI_ENSUREVISIBLE = 2232 +SC_FOLDFLAG_LINEBEFORE_EXPANDED = 0x0002 +SC_FOLDFLAG_LINEBEFORE_CONTRACTED = 0x0004 +SC_FOLDFLAG_LINEAFTER_EXPANDED = 0x0008 +SC_FOLDFLAG_LINEAFTER_CONTRACTED = 0x0010 +SC_FOLDFLAG_LEVELNUMBERS = 0x0040 +SC_FOLDFLAG_BOX = 0x0001 +SCI_SETFOLDFLAGS = 2233 +SCI_ENSUREVISIBLEENFORCEPOLICY = 2234 +SCI_SETTABINDENTS = 2260 +SCI_GETTABINDENTS = 2261 +SCI_SETBACKSPACEUNINDENTS = 2262 +SCI_GETBACKSPACEUNINDENTS = 2263 +SC_TIME_FOREVER = 10000000 +SCI_SETMOUSEDWELLTIME = 2264 +SCI_GETMOUSEDWELLTIME = 2265 +SCI_WORDSTARTPOSITION = 2266 +SCI_WORDENDPOSITION = 2267 +SC_WRAP_NONE = 0 +SC_WRAP_WORD = 1 +SC_WRAP_CHAR = 2 +SCI_SETWRAPMODE = 2268 +SCI_GETWRAPMODE = 2269 +SC_WRAPVISUALFLAG_NONE = 0x0000 +SC_WRAPVISUALFLAG_END = 0x0001 +SC_WRAPVISUALFLAG_START = 0x0002 +SCI_SETWRAPVISUALFLAGS = 2460 +SCI_GETWRAPVISUALFLAGS = 2461 +SC_WRAPVISUALFLAGLOC_DEFAULT = 0x0000 +SC_WRAPVISUALFLAGLOC_END_BY_TEXT = 0x0001 +SC_WRAPVISUALFLAGLOC_START_BY_TEXT = 0x0002 +SCI_SETWRAPVISUALFLAGSLOCATION = 2462 +SCI_GETWRAPVISUALFLAGSLOCATION = 2463 +SCI_SETWRAPSTARTINDENT = 2464 +SCI_GETWRAPSTARTINDENT = 2465 +SC_CACHE_NONE = 0 +SC_CACHE_CARET = 1 +SC_CACHE_PAGE = 2 +SC_CACHE_DOCUMENT = 3 +SCI_SETLAYOUTCACHE = 2272 +SCI_GETLAYOUTCACHE = 2273 +SCI_SETSCROLLWIDTH = 2274 +SCI_GETSCROLLWIDTH = 2275 +SCI_SETSCROLLWIDTHTRACKING = 2516 +SCI_GETSCROLLWIDTHTRACKING = 2517 +SCI_TEXTWIDTH = 2276 +SCI_SETENDATLASTLINE = 2277 +SCI_GETENDATLASTLINE = 2278 +SCI_TEXTHEIGHT = 2279 +SCI_SETVSCROLLBAR = 2280 +SCI_GETVSCROLLBAR = 2281 +SCI_APPENDTEXT = 2282 +SCI_GETTWOPHASEDRAW = 2283 +SCI_SETTWOPHASEDRAW = 2284 +SCI_TARGETFROMSELECTION = 2287 +SCI_LINESJOIN = 2288 +SCI_LINESSPLIT = 2289 +SCI_SETFOLDMARGINCOLOUR = 2290 +SCI_SETFOLDMARGINHICOLOUR = 2291 +SCI_LINEDOWN = 2300 +SCI_LINEDOWNEXTEND = 2301 +SCI_LINEUP = 2302 +SCI_LINEUPEXTEND = 2303 +SCI_CHARLEFT = 2304 +SCI_CHARLEFTEXTEND = 2305 +SCI_CHARRIGHT = 2306 +SCI_CHARRIGHTEXTEND = 2307 +SCI_WORDLEFT = 2308 +SCI_WORDLEFTEXTEND = 2309 +SCI_WORDRIGHT = 2310 +SCI_WORDRIGHTEXTEND = 2311 +SCI_HOME = 2312 +SCI_HOMEEXTEND = 2313 +SCI_LINEEND = 2314 +SCI_LINEENDEXTEND = 2315 +SCI_DOCUMENTSTART = 2316 +SCI_DOCUMENTSTARTEXTEND = 2317 +SCI_DOCUMENTEND = 2318 +SCI_DOCUMENTENDEXTEND = 2319 +SCI_PAGEUP = 2320 +SCI_PAGEUPEXTEND = 2321 +SCI_PAGEDOWN = 2322 +SCI_PAGEDOWNEXTEND = 2323 +SCI_EDITTOGGLEOVERTYPE = 2324 +SCI_CANCEL = 2325 +SCI_DELETEBACK = 2326 +SCI_TAB = 2327 +SCI_BACKTAB = 2328 +SCI_NEWLINE = 2329 +SCI_FORMFEED = 2330 +SCI_VCHOME = 2331 +SCI_VCHOMEEXTEND = 2332 +SCI_ZOOMIN = 2333 +SCI_ZOOMOUT = 2334 +SCI_DELWORDLEFT = 2335 +SCI_DELWORDRIGHT = 2336 +SCI_DELWORDRIGHTEND = 2518 +SCI_LINECUT = 2337 +SCI_LINEDELETE = 2338 +SCI_LINETRANSPOSE = 2339 +SCI_LINEDUPLICATE = 2404 +SCI_LOWERCASE = 2340 +SCI_UPPERCASE = 2341 +SCI_LINESCROLLDOWN = 2342 +SCI_LINESCROLLUP = 2343 +SCI_DELETEBACKNOTLINE = 2344 +SCI_HOMEDISPLAY = 2345 +SCI_HOMEDISPLAYEXTEND = 2346 +SCI_LINEENDDISPLAY = 2347 +SCI_LINEENDDISPLAYEXTEND = 2348 +SCI_HOMEWRAP = 2349 +SCI_HOMEWRAPEXTEND = 2450 +SCI_LINEENDWRAP = 2451 +SCI_LINEENDWRAPEXTEND = 2452 +SCI_VCHOMEWRAP = 2453 +SCI_VCHOMEWRAPEXTEND = 2454 +SCI_LINECOPY = 2455 +SCI_MOVECARETINSIDEVIEW = 2401 +SCI_LINELENGTH = 2350 +SCI_BRACEHIGHLIGHT = 2351 +SCI_BRACEBADLIGHT = 2352 +SCI_BRACEMATCH = 2353 +SCI_GETVIEWEOL = 2355 +SCI_SETVIEWEOL = 2356 +SCI_GETDOCPOINTER = 2357 +SCI_SETDOCPOINTER = 2358 +SCI_SETMODEVENTMASK = 2359 +EDGE_NONE = 0 +EDGE_LINE = 1 +EDGE_BACKGROUND = 2 +SCI_GETEDGECOLUMN = 2360 +SCI_SETEDGECOLUMN = 2361 +SCI_GETEDGEMODE = 2362 +SCI_SETEDGEMODE = 2363 +SCI_GETEDGECOLOUR = 2364 +SCI_SETEDGECOLOUR = 2365 +SCI_SEARCHANCHOR = 2366 +SCI_SEARCHNEXT = 2367 +SCI_SEARCHPREV = 2368 +SCI_LINESONSCREEN = 2370 +SCI_USEPOPUP = 2371 +SCI_SELECTIONISRECTANGLE = 2372 +SCI_SETZOOM = 2373 +SCI_GETZOOM = 2374 +SCI_CREATEDOCUMENT = 2375 +SCI_ADDREFDOCUMENT = 2376 +SCI_RELEASEDOCUMENT = 2377 +SCI_GETMODEVENTMASK = 2378 +SCI_SETFOCUS = 2380 +SCI_GETFOCUS = 2381 +SCI_SETSTATUS = 2382 +SCI_GETSTATUS = 2383 +SCI_SETMOUSEDOWNCAPTURES = 2384 +SCI_GETMOUSEDOWNCAPTURES = 2385 +SC_CURSORNORMAL = -1 +SC_CURSORWAIT = 4 +SCI_SETCURSOR = 2386 +SCI_GETCURSOR = 2387 +SCI_SETCONTROLCHARSYMBOL = 2388 +SCI_GETCONTROLCHARSYMBOL = 2389 +SCI_WORDPARTLEFT = 2390 +SCI_WORDPARTLEFTEXTEND = 2391 +SCI_WORDPARTRIGHT = 2392 +SCI_WORDPARTRIGHTEXTEND = 2393 +VISIBLE_SLOP = 0x01 +VISIBLE_STRICT = 0x04 +SCI_SETVISIBLEPOLICY = 2394 +SCI_DELLINELEFT = 2395 +SCI_DELLINERIGHT = 2396 +SCI_SETXOFFSET = 2397 +SCI_GETXOFFSET = 2398 +SCI_CHOOSECARETX = 2399 +SCI_GRABFOCUS = 2400 +CARET_SLOP = 0x01 +CARET_STRICT = 0x04 +CARET_JUMPS = 0x10 +CARET_EVEN = 0x08 +SCI_SETXCARETPOLICY = 2402 +SCI_SETYCARETPOLICY = 2403 +SCI_SETPRINTWRAPMODE = 2406 +SCI_GETPRINTWRAPMODE = 2407 +SCI_SETHOTSPOTACTIVEFORE = 2410 +SCI_GETHOTSPOTACTIVEFORE = 2494 +SCI_SETHOTSPOTACTIVEBACK = 2411 +SCI_GETHOTSPOTACTIVEBACK = 2495 +SCI_SETHOTSPOTACTIVEUNDERLINE = 2412 +SCI_GETHOTSPOTACTIVEUNDERLINE = 2496 +SCI_SETHOTSPOTSINGLELINE = 2421 +SCI_GETHOTSPOTSINGLELINE = 2497 +SCI_PARADOWN = 2413 +SCI_PARADOWNEXTEND = 2414 +SCI_PARAUP = 2415 +SCI_PARAUPEXTEND = 2416 +SCI_POSITIONBEFORE = 2417 +SCI_POSITIONAFTER = 2418 +SCI_COPYRANGE = 2419 +SCI_COPYTEXT = 2420 +SC_SEL_STREAM = 0 +SC_SEL_RECTANGLE = 1 +SC_SEL_LINES = 2 +SCI_SETSELECTIONMODE = 2422 +SCI_GETSELECTIONMODE = 2423 +SCI_GETLINESELSTARTPOSITION = 2424 +SCI_GETLINESELENDPOSITION = 2425 +SCI_LINEDOWNRECTEXTEND = 2426 +SCI_LINEUPRECTEXTEND = 2427 +SCI_CHARLEFTRECTEXTEND = 2428 +SCI_CHARRIGHTRECTEXTEND = 2429 +SCI_HOMERECTEXTEND = 2430 +SCI_VCHOMERECTEXTEND = 2431 +SCI_LINEENDRECTEXTEND = 2432 +SCI_PAGEUPRECTEXTEND = 2433 +SCI_PAGEDOWNRECTEXTEND = 2434 +SCI_STUTTEREDPAGEUP = 2435 +SCI_STUTTEREDPAGEUPEXTEND = 2436 +SCI_STUTTEREDPAGEDOWN = 2437 +SCI_STUTTEREDPAGEDOWNEXTEND = 2438 +SCI_WORDLEFTEND = 2439 +SCI_WORDLEFTENDEXTEND = 2440 +SCI_WORDRIGHTEND = 2441 +SCI_WORDRIGHTENDEXTEND = 2442 +SCI_SETWHITESPACECHARS = 2443 +SCI_SETCHARSDEFAULT = 2444 +SCI_AUTOCGETCURRENT = 2445 +SCI_ALLOCATE = 2446 +SCI_TARGETASUTF8 = 2447 +SCI_SETLENGTHFORENCODE = 2448 +SCI_ENCODEDFROMUTF8 = 2449 +SCI_FINDCOLUMN = 2456 +SCI_GETCARETSTICKY = 2457 +SCI_SETCARETSTICKY = 2458 +SCI_TOGGLECARETSTICKY = 2459 +SCI_SETPASTECONVERTENDINGS = 2467 +SCI_GETPASTECONVERTENDINGS = 2468 +SCI_SELECTIONDUPLICATE = 2469 +SC_ALPHA_TRANSPARENT = 0 +SC_ALPHA_OPAQUE = 255 +SC_ALPHA_NOALPHA = 256 +SCI_SETCARETLINEBACKALPHA = 2470 +SCI_GETCARETLINEBACKALPHA = 2471 +CARETSTYLE_INVISIBLE = 0 +CARETSTYLE_LINE = 1 +CARETSTYLE_BLOCK = 2 +SCI_SETCARETSTYLE = 2512 +SCI_GETCARETSTYLE = 2513 +SCI_SETINDICATORCURRENT = 2500 +SCI_GETINDICATORCURRENT = 2501 +SCI_SETINDICATORVALUE = 2502 +SCI_GETINDICATORVALUE = 2503 +SCI_INDICATORFILLRANGE = 2504 +SCI_INDICATORCLEARRANGE = 2505 +SCI_INDICATORALLONFOR = 2506 +SCI_INDICATORVALUEAT = 2507 +SCI_INDICATORSTART = 2508 +SCI_INDICATOREND = 2509 +SCI_SETPOSITIONCACHE = 2514 +SCI_GETPOSITIONCACHE = 2515 +SCI_COPYALLOWLINE = 2519 +SCI_GETCHARACTERPOINTER = 2520 +SCI_SETKEYSUNICODE = 2521 +SCI_GETKEYSUNICODE = 2522 +SCI_STARTRECORD = 3001 +SCI_STOPRECORD = 3002 +SCI_SETLEXER = 4001 +SCI_GETLEXER = 4002 +SCI_COLOURISE = 4003 +SCI_SETPROPERTY = 4004 +KEYWORDSET_MAX = 8 +SCI_SETKEYWORDS = 4005 +SCI_SETLEXERLANGUAGE = 4006 +SCI_LOADLEXERLIBRARY = 4007 +SCI_GETPROPERTY = 4008 +SCI_GETPROPERTYEXPANDED = 4009 +SCI_GETPROPERTYINT = 4010 +SCI_GETSTYLEBITSNEEDED = 4011 +SC_MOD_INSERTTEXT = 0x1 +SC_MOD_DELETETEXT = 0x2 +SC_MOD_CHANGESTYLE = 0x4 +SC_MOD_CHANGEFOLD = 0x8 +SC_PERFORMED_USER = 0x10 +SC_PERFORMED_UNDO = 0x20 +SC_PERFORMED_REDO = 0x40 +SC_MULTISTEPUNDOREDO = 0x80 +SC_LASTSTEPINUNDOREDO = 0x100 +SC_MOD_CHANGEMARKER = 0x200 +SC_MOD_BEFOREINSERT = 0x400 +SC_MOD_BEFOREDELETE = 0x800 +SC_MULTILINEUNDOREDO = 0x1000 +SC_STARTACTION = 0x2000 +SC_MOD_CHANGEINDICATOR = 0x4000 +SC_MOD_CHANGELINESTATE = 0x8000 +SC_MODEVENTMASKALL = 0xFFFF +SCEN_CHANGE = 768 +SCEN_SETFOCUS = 512 +SCEN_KILLFOCUS = 256 +SCK_DOWN = 300 +SCK_UP = 301 +SCK_LEFT = 302 +SCK_RIGHT = 303 +SCK_HOME = 304 +SCK_END = 305 +SCK_PRIOR = 306 +SCK_NEXT = 307 +SCK_DELETE = 308 +SCK_INSERT = 309 +SCK_ESCAPE = 7 +SCK_BACK = 8 +SCK_TAB = 9 +SCK_RETURN = 13 +SCK_ADD = 310 +SCK_SUBTRACT = 311 +SCK_DIVIDE = 312 +SCK_WIN = 313 +SCK_RWIN = 314 +SCK_MENU = 315 +SCMOD_NORM = 0 +SCMOD_SHIFT = 1 +SCMOD_CTRL = 2 +SCMOD_ALT = 4 +SCN_STYLENEEDED = 2000 +SCN_CHARADDED = 2001 +SCN_SAVEPOINTREACHED = 2002 +SCN_SAVEPOINTLEFT = 2003 +SCN_MODIFYATTEMPTRO = 2004 +SCN_KEY = 2005 +SCN_DOUBLECLICK = 2006 +SCN_UPDATEUI = 2007 +SCN_MODIFIED = 2008 +SCN_MACRORECORD = 2009 +SCN_MARGINCLICK = 2010 +SCN_NEEDSHOWN = 2011 +SCN_PAINTED = 2013 +SCN_USERLISTSELECTION = 2014 +SCN_URIDROPPED = 2015 +SCN_DWELLSTART = 2016 +SCN_DWELLEND = 2017 +SCN_ZOOM = 2018 +SCN_HOTSPOTCLICK = 2019 +SCN_HOTSPOTDOUBLECLICK = 2020 +SCN_CALLTIPCLICK = 2021 +SCN_AUTOCSELECTION = 2022 +SCN_INDICATORCLICK = 2023 +SCN_INDICATORRELEASE = 2024 +SCN_AUTOCCANCELLED = 2025 +SCI_SETCARETPOLICY = 2369 +CARET_CENTER = 0x02 +CARET_XEVEN = 0x08 +CARET_XJUMPS = 0x10 +SCN_POSCHANGED = 2012 +SCN_CHECKBRACE = 2007 +# Generated by h2py from Include\scilexer.h +SCLEX_CONTAINER = 0 +SCLEX_NULL = 1 +SCLEX_PYTHON = 2 +SCLEX_CPP = 3 +SCLEX_HTML = 4 +SCLEX_XML = 5 +SCLEX_PERL = 6 +SCLEX_SQL = 7 +SCLEX_VB = 8 +SCLEX_PROPERTIES = 9 +SCLEX_ERRORLIST = 10 +SCLEX_MAKEFILE = 11 +SCLEX_BATCH = 12 +SCLEX_XCODE = 13 +SCLEX_LATEX = 14 +SCLEX_LUA = 15 +SCLEX_DIFF = 16 +SCLEX_CONF = 17 +SCLEX_PASCAL = 18 +SCLEX_AVE = 19 +SCLEX_ADA = 20 +SCLEX_LISP = 21 +SCLEX_RUBY = 22 +SCLEX_EIFFEL = 23 +SCLEX_EIFFELKW = 24 +SCLEX_TCL = 25 +SCLEX_NNCRONTAB = 26 +SCLEX_BULLANT = 27 +SCLEX_VBSCRIPT = 28 +SCLEX_BAAN = 31 +SCLEX_MATLAB = 32 +SCLEX_SCRIPTOL = 33 +SCLEX_ASM = 34 +SCLEX_CPPNOCASE = 35 +SCLEX_FORTRAN = 36 +SCLEX_F77 = 37 +SCLEX_CSS = 38 +SCLEX_POV = 39 +SCLEX_LOUT = 40 +SCLEX_ESCRIPT = 41 +SCLEX_PS = 42 +SCLEX_NSIS = 43 +SCLEX_MMIXAL = 44 +SCLEX_CLW = 45 +SCLEX_CLWNOCASE = 46 +SCLEX_LOT = 47 +SCLEX_YAML = 48 +SCLEX_TEX = 49 +SCLEX_METAPOST = 50 +SCLEX_POWERBASIC = 51 +SCLEX_FORTH = 52 +SCLEX_ERLANG = 53 +SCLEX_OCTAVE = 54 +SCLEX_MSSQL = 55 +SCLEX_VERILOG = 56 +SCLEX_KIX = 57 +SCLEX_GUI4CLI = 58 +SCLEX_SPECMAN = 59 +SCLEX_AU3 = 60 +SCLEX_APDL = 61 +SCLEX_BASH = 62 +SCLEX_ASN1 = 63 +SCLEX_VHDL = 64 +SCLEX_CAML = 65 +SCLEX_BLITZBASIC = 66 +SCLEX_PUREBASIC = 67 +SCLEX_HASKELL = 68 +SCLEX_PHPSCRIPT = 69 +SCLEX_TADS3 = 70 +SCLEX_REBOL = 71 +SCLEX_SMALLTALK = 72 +SCLEX_FLAGSHIP = 73 +SCLEX_CSOUND = 74 +SCLEX_FREEBASIC = 75 +SCLEX_INNOSETUP = 76 +SCLEX_OPAL = 77 +SCLEX_SPICE = 78 +SCLEX_D = 79 +SCLEX_CMAKE = 80 +SCLEX_GAP = 81 +SCLEX_PLM = 82 +SCLEX_PROGRESS = 83 +SCLEX_ABAQUS = 84 +SCLEX_ASYMPTOTE = 85 +SCLEX_R = 86 +SCLEX_MAGIK = 87 +SCLEX_POWERSHELL = 88 +SCLEX_MYSQL = 89 +SCLEX_PO = 90 +SCLEX_AUTOMATIC = 1000 +SCE_P_DEFAULT = 0 +SCE_P_COMMENTLINE = 1 +SCE_P_NUMBER = 2 +SCE_P_STRING = 3 +SCE_P_CHARACTER = 4 +SCE_P_WORD = 5 +SCE_P_TRIPLE = 6 +SCE_P_TRIPLEDOUBLE = 7 +SCE_P_CLASSNAME = 8 +SCE_P_DEFNAME = 9 +SCE_P_OPERATOR = 10 +SCE_P_IDENTIFIER = 11 +SCE_P_COMMENTBLOCK = 12 +SCE_P_STRINGEOL = 13 +SCE_P_WORD2 = 14 +SCE_P_DECORATOR = 15 +SCE_C_DEFAULT = 0 +SCE_C_COMMENT = 1 +SCE_C_COMMENTLINE = 2 +SCE_C_COMMENTDOC = 3 +SCE_C_NUMBER = 4 +SCE_C_WORD = 5 +SCE_C_STRING = 6 +SCE_C_CHARACTER = 7 +SCE_C_UUID = 8 +SCE_C_PREPROCESSOR = 9 +SCE_C_OPERATOR = 10 +SCE_C_IDENTIFIER = 11 +SCE_C_STRINGEOL = 12 +SCE_C_VERBATIM = 13 +SCE_C_REGEX = 14 +SCE_C_COMMENTLINEDOC = 15 +SCE_C_WORD2 = 16 +SCE_C_COMMENTDOCKEYWORD = 17 +SCE_C_COMMENTDOCKEYWORDERROR = 18 +SCE_C_GLOBALCLASS = 19 +SCE_D_DEFAULT = 0 +SCE_D_COMMENT = 1 +SCE_D_COMMENTLINE = 2 +SCE_D_COMMENTDOC = 3 +SCE_D_COMMENTNESTED = 4 +SCE_D_NUMBER = 5 +SCE_D_WORD = 6 +SCE_D_WORD2 = 7 +SCE_D_WORD3 = 8 +SCE_D_TYPEDEF = 9 +SCE_D_STRING = 10 +SCE_D_STRINGEOL = 11 +SCE_D_CHARACTER = 12 +SCE_D_OPERATOR = 13 +SCE_D_IDENTIFIER = 14 +SCE_D_COMMENTLINEDOC = 15 +SCE_D_COMMENTDOCKEYWORD = 16 +SCE_D_COMMENTDOCKEYWORDERROR = 17 +SCE_TCL_DEFAULT = 0 +SCE_TCL_COMMENT = 1 +SCE_TCL_COMMENTLINE = 2 +SCE_TCL_NUMBER = 3 +SCE_TCL_WORD_IN_QUOTE = 4 +SCE_TCL_IN_QUOTE = 5 +SCE_TCL_OPERATOR = 6 +SCE_TCL_IDENTIFIER = 7 +SCE_TCL_SUBSTITUTION = 8 +SCE_TCL_SUB_BRACE = 9 +SCE_TCL_MODIFIER = 10 +SCE_TCL_EXPAND = 11 +SCE_TCL_WORD = 12 +SCE_TCL_WORD2 = 13 +SCE_TCL_WORD3 = 14 +SCE_TCL_WORD4 = 15 +SCE_TCL_WORD5 = 16 +SCE_TCL_WORD6 = 17 +SCE_TCL_WORD7 = 18 +SCE_TCL_WORD8 = 19 +SCE_TCL_COMMENT_BOX = 20 +SCE_TCL_BLOCK_COMMENT = 21 +SCE_H_DEFAULT = 0 +SCE_H_TAG = 1 +SCE_H_TAGUNKNOWN = 2 +SCE_H_ATTRIBUTE = 3 +SCE_H_ATTRIBUTEUNKNOWN = 4 +SCE_H_NUMBER = 5 +SCE_H_DOUBLESTRING = 6 +SCE_H_SINGLESTRING = 7 +SCE_H_OTHER = 8 +SCE_H_COMMENT = 9 +SCE_H_ENTITY = 10 +SCE_H_TAGEND = 11 +SCE_H_XMLSTART = 12 +SCE_H_XMLEND = 13 +SCE_H_SCRIPT = 14 +SCE_H_ASP = 15 +SCE_H_ASPAT = 16 +SCE_H_CDATA = 17 +SCE_H_QUESTION = 18 +SCE_H_VALUE = 19 +SCE_H_XCCOMMENT = 20 +SCE_H_SGML_DEFAULT = 21 +SCE_H_SGML_COMMAND = 22 +SCE_H_SGML_1ST_PARAM = 23 +SCE_H_SGML_DOUBLESTRING = 24 +SCE_H_SGML_SIMPLESTRING = 25 +SCE_H_SGML_ERROR = 26 +SCE_H_SGML_SPECIAL = 27 +SCE_H_SGML_ENTITY = 28 +SCE_H_SGML_COMMENT = 29 +SCE_H_SGML_1ST_PARAM_COMMENT = 30 +SCE_H_SGML_BLOCK_DEFAULT = 31 +SCE_HJ_START = 40 +SCE_HJ_DEFAULT = 41 +SCE_HJ_COMMENT = 42 +SCE_HJ_COMMENTLINE = 43 +SCE_HJ_COMMENTDOC = 44 +SCE_HJ_NUMBER = 45 +SCE_HJ_WORD = 46 +SCE_HJ_KEYWORD = 47 +SCE_HJ_DOUBLESTRING = 48 +SCE_HJ_SINGLESTRING = 49 +SCE_HJ_SYMBOLS = 50 +SCE_HJ_STRINGEOL = 51 +SCE_HJ_REGEX = 52 +SCE_HJA_START = 55 +SCE_HJA_DEFAULT = 56 +SCE_HJA_COMMENT = 57 +SCE_HJA_COMMENTLINE = 58 +SCE_HJA_COMMENTDOC = 59 +SCE_HJA_NUMBER = 60 +SCE_HJA_WORD = 61 +SCE_HJA_KEYWORD = 62 +SCE_HJA_DOUBLESTRING = 63 +SCE_HJA_SINGLESTRING = 64 +SCE_HJA_SYMBOLS = 65 +SCE_HJA_STRINGEOL = 66 +SCE_HJA_REGEX = 67 +SCE_HB_START = 70 +SCE_HB_DEFAULT = 71 +SCE_HB_COMMENTLINE = 72 +SCE_HB_NUMBER = 73 +SCE_HB_WORD = 74 +SCE_HB_STRING = 75 +SCE_HB_IDENTIFIER = 76 +SCE_HB_STRINGEOL = 77 +SCE_HBA_START = 80 +SCE_HBA_DEFAULT = 81 +SCE_HBA_COMMENTLINE = 82 +SCE_HBA_NUMBER = 83 +SCE_HBA_WORD = 84 +SCE_HBA_STRING = 85 +SCE_HBA_IDENTIFIER = 86 +SCE_HBA_STRINGEOL = 87 +SCE_HP_START = 90 +SCE_HP_DEFAULT = 91 +SCE_HP_COMMENTLINE = 92 +SCE_HP_NUMBER = 93 +SCE_HP_STRING = 94 +SCE_HP_CHARACTER = 95 +SCE_HP_WORD = 96 +SCE_HP_TRIPLE = 97 +SCE_HP_TRIPLEDOUBLE = 98 +SCE_HP_CLASSNAME = 99 +SCE_HP_DEFNAME = 100 +SCE_HP_OPERATOR = 101 +SCE_HP_IDENTIFIER = 102 +SCE_HPHP_COMPLEX_VARIABLE = 104 +SCE_HPA_START = 105 +SCE_HPA_DEFAULT = 106 +SCE_HPA_COMMENTLINE = 107 +SCE_HPA_NUMBER = 108 +SCE_HPA_STRING = 109 +SCE_HPA_CHARACTER = 110 +SCE_HPA_WORD = 111 +SCE_HPA_TRIPLE = 112 +SCE_HPA_TRIPLEDOUBLE = 113 +SCE_HPA_CLASSNAME = 114 +SCE_HPA_DEFNAME = 115 +SCE_HPA_OPERATOR = 116 +SCE_HPA_IDENTIFIER = 117 +SCE_HPHP_DEFAULT = 118 +SCE_HPHP_HSTRING = 119 +SCE_HPHP_SIMPLESTRING = 120 +SCE_HPHP_WORD = 121 +SCE_HPHP_NUMBER = 122 +SCE_HPHP_VARIABLE = 123 +SCE_HPHP_COMMENT = 124 +SCE_HPHP_COMMENTLINE = 125 +SCE_HPHP_HSTRING_VARIABLE = 126 +SCE_HPHP_OPERATOR = 127 +SCE_PL_DEFAULT = 0 +SCE_PL_ERROR = 1 +SCE_PL_COMMENTLINE = 2 +SCE_PL_POD = 3 +SCE_PL_NUMBER = 4 +SCE_PL_WORD = 5 +SCE_PL_STRING = 6 +SCE_PL_CHARACTER = 7 +SCE_PL_PUNCTUATION = 8 +SCE_PL_PREPROCESSOR = 9 +SCE_PL_OPERATOR = 10 +SCE_PL_IDENTIFIER = 11 +SCE_PL_SCALAR = 12 +SCE_PL_ARRAY = 13 +SCE_PL_HASH = 14 +SCE_PL_SYMBOLTABLE = 15 +SCE_PL_VARIABLE_INDEXER = 16 +SCE_PL_REGEX = 17 +SCE_PL_REGSUBST = 18 +SCE_PL_LONGQUOTE = 19 +SCE_PL_BACKTICKS = 20 +SCE_PL_DATASECTION = 21 +SCE_PL_HERE_DELIM = 22 +SCE_PL_HERE_Q = 23 +SCE_PL_HERE_QQ = 24 +SCE_PL_HERE_QX = 25 +SCE_PL_STRING_Q = 26 +SCE_PL_STRING_QQ = 27 +SCE_PL_STRING_QX = 28 +SCE_PL_STRING_QR = 29 +SCE_PL_STRING_QW = 30 +SCE_PL_POD_VERB = 31 +SCE_PL_SUB_PROTOTYPE = 40 +SCE_PL_FORMAT_IDENT = 41 +SCE_PL_FORMAT = 42 +SCE_RB_DEFAULT = 0 +SCE_RB_ERROR = 1 +SCE_RB_COMMENTLINE = 2 +SCE_RB_POD = 3 +SCE_RB_NUMBER = 4 +SCE_RB_WORD = 5 +SCE_RB_STRING = 6 +SCE_RB_CHARACTER = 7 +SCE_RB_CLASSNAME = 8 +SCE_RB_DEFNAME = 9 +SCE_RB_OPERATOR = 10 +SCE_RB_IDENTIFIER = 11 +SCE_RB_REGEX = 12 +SCE_RB_GLOBAL = 13 +SCE_RB_SYMBOL = 14 +SCE_RB_MODULE_NAME = 15 +SCE_RB_INSTANCE_VAR = 16 +SCE_RB_CLASS_VAR = 17 +SCE_RB_BACKTICKS = 18 +SCE_RB_DATASECTION = 19 +SCE_RB_HERE_DELIM = 20 +SCE_RB_HERE_Q = 21 +SCE_RB_HERE_QQ = 22 +SCE_RB_HERE_QX = 23 +SCE_RB_STRING_Q = 24 +SCE_RB_STRING_QQ = 25 +SCE_RB_STRING_QX = 26 +SCE_RB_STRING_QR = 27 +SCE_RB_STRING_QW = 28 +SCE_RB_WORD_DEMOTED = 29 +SCE_RB_STDIN = 30 +SCE_RB_STDOUT = 31 +SCE_RB_STDERR = 40 +SCE_RB_UPPER_BOUND = 41 +SCE_B_DEFAULT = 0 +SCE_B_COMMENT = 1 +SCE_B_NUMBER = 2 +SCE_B_KEYWORD = 3 +SCE_B_STRING = 4 +SCE_B_PREPROCESSOR = 5 +SCE_B_OPERATOR = 6 +SCE_B_IDENTIFIER = 7 +SCE_B_DATE = 8 +SCE_B_STRINGEOL = 9 +SCE_B_KEYWORD2 = 10 +SCE_B_KEYWORD3 = 11 +SCE_B_KEYWORD4 = 12 +SCE_B_CONSTANT = 13 +SCE_B_ASM = 14 +SCE_B_LABEL = 15 +SCE_B_ERROR = 16 +SCE_B_HEXNUMBER = 17 +SCE_B_BINNUMBER = 18 +SCE_PROPS_DEFAULT = 0 +SCE_PROPS_COMMENT = 1 +SCE_PROPS_SECTION = 2 +SCE_PROPS_ASSIGNMENT = 3 +SCE_PROPS_DEFVAL = 4 +SCE_PROPS_KEY = 5 +SCE_L_DEFAULT = 0 +SCE_L_COMMAND = 1 +SCE_L_TAG = 2 +SCE_L_MATH = 3 +SCE_L_COMMENT = 4 +SCE_LUA_DEFAULT = 0 +SCE_LUA_COMMENT = 1 +SCE_LUA_COMMENTLINE = 2 +SCE_LUA_COMMENTDOC = 3 +SCE_LUA_NUMBER = 4 +SCE_LUA_WORD = 5 +SCE_LUA_STRING = 6 +SCE_LUA_CHARACTER = 7 +SCE_LUA_LITERALSTRING = 8 +SCE_LUA_PREPROCESSOR = 9 +SCE_LUA_OPERATOR = 10 +SCE_LUA_IDENTIFIER = 11 +SCE_LUA_STRINGEOL = 12 +SCE_LUA_WORD2 = 13 +SCE_LUA_WORD3 = 14 +SCE_LUA_WORD4 = 15 +SCE_LUA_WORD5 = 16 +SCE_LUA_WORD6 = 17 +SCE_LUA_WORD7 = 18 +SCE_LUA_WORD8 = 19 +SCE_ERR_DEFAULT = 0 +SCE_ERR_PYTHON = 1 +SCE_ERR_GCC = 2 +SCE_ERR_MS = 3 +SCE_ERR_CMD = 4 +SCE_ERR_BORLAND = 5 +SCE_ERR_PERL = 6 +SCE_ERR_NET = 7 +SCE_ERR_LUA = 8 +SCE_ERR_CTAG = 9 +SCE_ERR_DIFF_CHANGED = 10 +SCE_ERR_DIFF_ADDITION = 11 +SCE_ERR_DIFF_DELETION = 12 +SCE_ERR_DIFF_MESSAGE = 13 +SCE_ERR_PHP = 14 +SCE_ERR_ELF = 15 +SCE_ERR_IFC = 16 +SCE_ERR_IFORT = 17 +SCE_ERR_ABSF = 18 +SCE_ERR_TIDY = 19 +SCE_ERR_JAVA_STACK = 20 +SCE_ERR_VALUE = 21 +SCE_BAT_DEFAULT = 0 +SCE_BAT_COMMENT = 1 +SCE_BAT_WORD = 2 +SCE_BAT_LABEL = 3 +SCE_BAT_HIDE = 4 +SCE_BAT_COMMAND = 5 +SCE_BAT_IDENTIFIER = 6 +SCE_BAT_OPERATOR = 7 +SCE_MAKE_DEFAULT = 0 +SCE_MAKE_COMMENT = 1 +SCE_MAKE_PREPROCESSOR = 2 +SCE_MAKE_IDENTIFIER = 3 +SCE_MAKE_OPERATOR = 4 +SCE_MAKE_TARGET = 5 +SCE_MAKE_IDEOL = 9 +SCE_DIFF_DEFAULT = 0 +SCE_DIFF_COMMENT = 1 +SCE_DIFF_COMMAND = 2 +SCE_DIFF_HEADER = 3 +SCE_DIFF_POSITION = 4 +SCE_DIFF_DELETED = 5 +SCE_DIFF_ADDED = 6 +SCE_DIFF_CHANGED = 7 +SCE_CONF_DEFAULT = 0 +SCE_CONF_COMMENT = 1 +SCE_CONF_NUMBER = 2 +SCE_CONF_IDENTIFIER = 3 +SCE_CONF_EXTENSION = 4 +SCE_CONF_PARAMETER = 5 +SCE_CONF_STRING = 6 +SCE_CONF_OPERATOR = 7 +SCE_CONF_IP = 8 +SCE_CONF_DIRECTIVE = 9 +SCE_AVE_DEFAULT = 0 +SCE_AVE_COMMENT = 1 +SCE_AVE_NUMBER = 2 +SCE_AVE_WORD = 3 +SCE_AVE_STRING = 6 +SCE_AVE_ENUM = 7 +SCE_AVE_STRINGEOL = 8 +SCE_AVE_IDENTIFIER = 9 +SCE_AVE_OPERATOR = 10 +SCE_AVE_WORD1 = 11 +SCE_AVE_WORD2 = 12 +SCE_AVE_WORD3 = 13 +SCE_AVE_WORD4 = 14 +SCE_AVE_WORD5 = 15 +SCE_AVE_WORD6 = 16 +SCE_ADA_DEFAULT = 0 +SCE_ADA_WORD = 1 +SCE_ADA_IDENTIFIER = 2 +SCE_ADA_NUMBER = 3 +SCE_ADA_DELIMITER = 4 +SCE_ADA_CHARACTER = 5 +SCE_ADA_CHARACTEREOL = 6 +SCE_ADA_STRING = 7 +SCE_ADA_STRINGEOL = 8 +SCE_ADA_LABEL = 9 +SCE_ADA_COMMENTLINE = 10 +SCE_ADA_ILLEGAL = 11 +SCE_BAAN_DEFAULT = 0 +SCE_BAAN_COMMENT = 1 +SCE_BAAN_COMMENTDOC = 2 +SCE_BAAN_NUMBER = 3 +SCE_BAAN_WORD = 4 +SCE_BAAN_STRING = 5 +SCE_BAAN_PREPROCESSOR = 6 +SCE_BAAN_OPERATOR = 7 +SCE_BAAN_IDENTIFIER = 8 +SCE_BAAN_STRINGEOL = 9 +SCE_BAAN_WORD2 = 10 +SCE_LISP_DEFAULT = 0 +SCE_LISP_COMMENT = 1 +SCE_LISP_NUMBER = 2 +SCE_LISP_KEYWORD = 3 +SCE_LISP_KEYWORD_KW = 4 +SCE_LISP_SYMBOL = 5 +SCE_LISP_STRING = 6 +SCE_LISP_STRINGEOL = 8 +SCE_LISP_IDENTIFIER = 9 +SCE_LISP_OPERATOR = 10 +SCE_LISP_SPECIAL = 11 +SCE_LISP_MULTI_COMMENT = 12 +SCE_EIFFEL_DEFAULT = 0 +SCE_EIFFEL_COMMENTLINE = 1 +SCE_EIFFEL_NUMBER = 2 +SCE_EIFFEL_WORD = 3 +SCE_EIFFEL_STRING = 4 +SCE_EIFFEL_CHARACTER = 5 +SCE_EIFFEL_OPERATOR = 6 +SCE_EIFFEL_IDENTIFIER = 7 +SCE_EIFFEL_STRINGEOL = 8 +SCE_NNCRONTAB_DEFAULT = 0 +SCE_NNCRONTAB_COMMENT = 1 +SCE_NNCRONTAB_TASK = 2 +SCE_NNCRONTAB_SECTION = 3 +SCE_NNCRONTAB_KEYWORD = 4 +SCE_NNCRONTAB_MODIFIER = 5 +SCE_NNCRONTAB_ASTERISK = 6 +SCE_NNCRONTAB_NUMBER = 7 +SCE_NNCRONTAB_STRING = 8 +SCE_NNCRONTAB_ENVIRONMENT = 9 +SCE_NNCRONTAB_IDENTIFIER = 10 +SCE_FORTH_DEFAULT = 0 +SCE_FORTH_COMMENT = 1 +SCE_FORTH_COMMENT_ML = 2 +SCE_FORTH_IDENTIFIER = 3 +SCE_FORTH_CONTROL = 4 +SCE_FORTH_KEYWORD = 5 +SCE_FORTH_DEFWORD = 6 +SCE_FORTH_PREWORD1 = 7 +SCE_FORTH_PREWORD2 = 8 +SCE_FORTH_NUMBER = 9 +SCE_FORTH_STRING = 10 +SCE_FORTH_LOCALE = 11 +SCE_MATLAB_DEFAULT = 0 +SCE_MATLAB_COMMENT = 1 +SCE_MATLAB_COMMAND = 2 +SCE_MATLAB_NUMBER = 3 +SCE_MATLAB_KEYWORD = 4 +SCE_MATLAB_STRING = 5 +SCE_MATLAB_OPERATOR = 6 +SCE_MATLAB_IDENTIFIER = 7 +SCE_MATLAB_DOUBLEQUOTESTRING = 8 +SCE_SCRIPTOL_DEFAULT = 0 +SCE_SCRIPTOL_WHITE = 1 +SCE_SCRIPTOL_COMMENTLINE = 2 +SCE_SCRIPTOL_PERSISTENT = 3 +SCE_SCRIPTOL_CSTYLE = 4 +SCE_SCRIPTOL_COMMENTBLOCK = 5 +SCE_SCRIPTOL_NUMBER = 6 +SCE_SCRIPTOL_STRING = 7 +SCE_SCRIPTOL_CHARACTER = 8 +SCE_SCRIPTOL_STRINGEOL = 9 +SCE_SCRIPTOL_KEYWORD = 10 +SCE_SCRIPTOL_OPERATOR = 11 +SCE_SCRIPTOL_IDENTIFIER = 12 +SCE_SCRIPTOL_TRIPLE = 13 +SCE_SCRIPTOL_CLASSNAME = 14 +SCE_SCRIPTOL_PREPROCESSOR = 15 +SCE_ASM_DEFAULT = 0 +SCE_ASM_COMMENT = 1 +SCE_ASM_NUMBER = 2 +SCE_ASM_STRING = 3 +SCE_ASM_OPERATOR = 4 +SCE_ASM_IDENTIFIER = 5 +SCE_ASM_CPUINSTRUCTION = 6 +SCE_ASM_MATHINSTRUCTION = 7 +SCE_ASM_REGISTER = 8 +SCE_ASM_DIRECTIVE = 9 +SCE_ASM_DIRECTIVEOPERAND = 10 +SCE_ASM_COMMENTBLOCK = 11 +SCE_ASM_CHARACTER = 12 +SCE_ASM_STRINGEOL = 13 +SCE_ASM_EXTINSTRUCTION = 14 +SCE_F_DEFAULT = 0 +SCE_F_COMMENT = 1 +SCE_F_NUMBER = 2 +SCE_F_STRING1 = 3 +SCE_F_STRING2 = 4 +SCE_F_STRINGEOL = 5 +SCE_F_OPERATOR = 6 +SCE_F_IDENTIFIER = 7 +SCE_F_WORD = 8 +SCE_F_WORD2 = 9 +SCE_F_WORD3 = 10 +SCE_F_PREPROCESSOR = 11 +SCE_F_OPERATOR2 = 12 +SCE_F_LABEL = 13 +SCE_F_CONTINUATION = 14 +SCE_CSS_DEFAULT = 0 +SCE_CSS_TAG = 1 +SCE_CSS_CLASS = 2 +SCE_CSS_PSEUDOCLASS = 3 +SCE_CSS_UNKNOWN_PSEUDOCLASS = 4 +SCE_CSS_OPERATOR = 5 +SCE_CSS_IDENTIFIER = 6 +SCE_CSS_UNKNOWN_IDENTIFIER = 7 +SCE_CSS_VALUE = 8 +SCE_CSS_COMMENT = 9 +SCE_CSS_ID = 10 +SCE_CSS_IMPORTANT = 11 +SCE_CSS_DIRECTIVE = 12 +SCE_CSS_DOUBLESTRING = 13 +SCE_CSS_SINGLESTRING = 14 +SCE_CSS_IDENTIFIER2 = 15 +SCE_CSS_ATTRIBUTE = 16 +SCE_CSS_IDENTIFIER3 = 17 +SCE_CSS_PSEUDOELEMENT = 18 +SCE_CSS_EXTENDED_IDENTIFIER = 19 +SCE_CSS_EXTENDED_PSEUDOCLASS = 20 +SCE_CSS_EXTENDED_PSEUDOELEMENT = 21 +SCE_POV_DEFAULT = 0 +SCE_POV_COMMENT = 1 +SCE_POV_COMMENTLINE = 2 +SCE_POV_NUMBER = 3 +SCE_POV_OPERATOR = 4 +SCE_POV_IDENTIFIER = 5 +SCE_POV_STRING = 6 +SCE_POV_STRINGEOL = 7 +SCE_POV_DIRECTIVE = 8 +SCE_POV_BADDIRECTIVE = 9 +SCE_POV_WORD2 = 10 +SCE_POV_WORD3 = 11 +SCE_POV_WORD4 = 12 +SCE_POV_WORD5 = 13 +SCE_POV_WORD6 = 14 +SCE_POV_WORD7 = 15 +SCE_POV_WORD8 = 16 +SCE_LOUT_DEFAULT = 0 +SCE_LOUT_COMMENT = 1 +SCE_LOUT_NUMBER = 2 +SCE_LOUT_WORD = 3 +SCE_LOUT_WORD2 = 4 +SCE_LOUT_WORD3 = 5 +SCE_LOUT_WORD4 = 6 +SCE_LOUT_STRING = 7 +SCE_LOUT_OPERATOR = 8 +SCE_LOUT_IDENTIFIER = 9 +SCE_LOUT_STRINGEOL = 10 +SCE_ESCRIPT_DEFAULT = 0 +SCE_ESCRIPT_COMMENT = 1 +SCE_ESCRIPT_COMMENTLINE = 2 +SCE_ESCRIPT_COMMENTDOC = 3 +SCE_ESCRIPT_NUMBER = 4 +SCE_ESCRIPT_WORD = 5 +SCE_ESCRIPT_STRING = 6 +SCE_ESCRIPT_OPERATOR = 7 +SCE_ESCRIPT_IDENTIFIER = 8 +SCE_ESCRIPT_BRACE = 9 +SCE_ESCRIPT_WORD2 = 10 +SCE_ESCRIPT_WORD3 = 11 +SCE_PS_DEFAULT = 0 +SCE_PS_COMMENT = 1 +SCE_PS_DSC_COMMENT = 2 +SCE_PS_DSC_VALUE = 3 +SCE_PS_NUMBER = 4 +SCE_PS_NAME = 5 +SCE_PS_KEYWORD = 6 +SCE_PS_LITERAL = 7 +SCE_PS_IMMEVAL = 8 +SCE_PS_PAREN_ARRAY = 9 +SCE_PS_PAREN_DICT = 10 +SCE_PS_PAREN_PROC = 11 +SCE_PS_TEXT = 12 +SCE_PS_HEXSTRING = 13 +SCE_PS_BASE85STRING = 14 +SCE_PS_BADSTRINGCHAR = 15 +SCE_NSIS_DEFAULT = 0 +SCE_NSIS_COMMENT = 1 +SCE_NSIS_STRINGDQ = 2 +SCE_NSIS_STRINGLQ = 3 +SCE_NSIS_STRINGRQ = 4 +SCE_NSIS_FUNCTION = 5 +SCE_NSIS_VARIABLE = 6 +SCE_NSIS_LABEL = 7 +SCE_NSIS_USERDEFINED = 8 +SCE_NSIS_SECTIONDEF = 9 +SCE_NSIS_SUBSECTIONDEF = 10 +SCE_NSIS_IFDEFINEDEF = 11 +SCE_NSIS_MACRODEF = 12 +SCE_NSIS_STRINGVAR = 13 +SCE_NSIS_NUMBER = 14 +SCE_NSIS_SECTIONGROUP = 15 +SCE_NSIS_PAGEEX = 16 +SCE_NSIS_FUNCTIONDEF = 17 +SCE_NSIS_COMMENTBOX = 18 +SCE_MMIXAL_LEADWS = 0 +SCE_MMIXAL_COMMENT = 1 +SCE_MMIXAL_LABEL = 2 +SCE_MMIXAL_OPCODE = 3 +SCE_MMIXAL_OPCODE_PRE = 4 +SCE_MMIXAL_OPCODE_VALID = 5 +SCE_MMIXAL_OPCODE_UNKNOWN = 6 +SCE_MMIXAL_OPCODE_POST = 7 +SCE_MMIXAL_OPERANDS = 8 +SCE_MMIXAL_NUMBER = 9 +SCE_MMIXAL_REF = 10 +SCE_MMIXAL_CHAR = 11 +SCE_MMIXAL_STRING = 12 +SCE_MMIXAL_REGISTER = 13 +SCE_MMIXAL_HEX = 14 +SCE_MMIXAL_OPERATOR = 15 +SCE_MMIXAL_SYMBOL = 16 +SCE_MMIXAL_INCLUDE = 17 +SCE_CLW_DEFAULT = 0 +SCE_CLW_LABEL = 1 +SCE_CLW_COMMENT = 2 +SCE_CLW_STRING = 3 +SCE_CLW_USER_IDENTIFIER = 4 +SCE_CLW_INTEGER_CONSTANT = 5 +SCE_CLW_REAL_CONSTANT = 6 +SCE_CLW_PICTURE_STRING = 7 +SCE_CLW_KEYWORD = 8 +SCE_CLW_COMPILER_DIRECTIVE = 9 +SCE_CLW_RUNTIME_EXPRESSIONS = 10 +SCE_CLW_BUILTIN_PROCEDURES_FUNCTION = 11 +SCE_CLW_STRUCTURE_DATA_TYPE = 12 +SCE_CLW_ATTRIBUTE = 13 +SCE_CLW_STANDARD_EQUATE = 14 +SCE_CLW_ERROR = 15 +SCE_CLW_DEPRECATED = 16 +SCE_LOT_DEFAULT = 0 +SCE_LOT_HEADER = 1 +SCE_LOT_BREAK = 2 +SCE_LOT_SET = 3 +SCE_LOT_PASS = 4 +SCE_LOT_FAIL = 5 +SCE_LOT_ABORT = 6 +SCE_YAML_DEFAULT = 0 +SCE_YAML_COMMENT = 1 +SCE_YAML_IDENTIFIER = 2 +SCE_YAML_KEYWORD = 3 +SCE_YAML_NUMBER = 4 +SCE_YAML_REFERENCE = 5 +SCE_YAML_DOCUMENT = 6 +SCE_YAML_TEXT = 7 +SCE_YAML_ERROR = 8 +SCE_YAML_OPERATOR = 9 +SCE_TEX_DEFAULT = 0 +SCE_TEX_SPECIAL = 1 +SCE_TEX_GROUP = 2 +SCE_TEX_SYMBOL = 3 +SCE_TEX_COMMAND = 4 +SCE_TEX_TEXT = 5 +SCE_METAPOST_DEFAULT = 0 +SCE_METAPOST_SPECIAL = 1 +SCE_METAPOST_GROUP = 2 +SCE_METAPOST_SYMBOL = 3 +SCE_METAPOST_COMMAND = 4 +SCE_METAPOST_TEXT = 5 +SCE_METAPOST_EXTRA = 6 +SCE_ERLANG_DEFAULT = 0 +SCE_ERLANG_COMMENT = 1 +SCE_ERLANG_VARIABLE = 2 +SCE_ERLANG_NUMBER = 3 +SCE_ERLANG_KEYWORD = 4 +SCE_ERLANG_STRING = 5 +SCE_ERLANG_OPERATOR = 6 +SCE_ERLANG_ATOM = 7 +SCE_ERLANG_FUNCTION_NAME = 8 +SCE_ERLANG_CHARACTER = 9 +SCE_ERLANG_MACRO = 10 +SCE_ERLANG_RECORD = 11 +SCE_ERLANG_SEPARATOR = 12 +SCE_ERLANG_NODE_NAME = 13 +SCE_ERLANG_UNKNOWN = 31 +SCE_MSSQL_DEFAULT = 0 +SCE_MSSQL_COMMENT = 1 +SCE_MSSQL_LINE_COMMENT = 2 +SCE_MSSQL_NUMBER = 3 +SCE_MSSQL_STRING = 4 +SCE_MSSQL_OPERATOR = 5 +SCE_MSSQL_IDENTIFIER = 6 +SCE_MSSQL_VARIABLE = 7 +SCE_MSSQL_COLUMN_NAME = 8 +SCE_MSSQL_STATEMENT = 9 +SCE_MSSQL_DATATYPE = 10 +SCE_MSSQL_SYSTABLE = 11 +SCE_MSSQL_GLOBAL_VARIABLE = 12 +SCE_MSSQL_FUNCTION = 13 +SCE_MSSQL_STORED_PROCEDURE = 14 +SCE_MSSQL_DEFAULT_PREF_DATATYPE = 15 +SCE_MSSQL_COLUMN_NAME_2 = 16 +SCE_V_DEFAULT = 0 +SCE_V_COMMENT = 1 +SCE_V_COMMENTLINE = 2 +SCE_V_COMMENTLINEBANG = 3 +SCE_V_NUMBER = 4 +SCE_V_WORD = 5 +SCE_V_STRING = 6 +SCE_V_WORD2 = 7 +SCE_V_WORD3 = 8 +SCE_V_PREPROCESSOR = 9 +SCE_V_OPERATOR = 10 +SCE_V_IDENTIFIER = 11 +SCE_V_STRINGEOL = 12 +SCE_V_USER = 19 +SCE_KIX_DEFAULT = 0 +SCE_KIX_COMMENT = 1 +SCE_KIX_STRING1 = 2 +SCE_KIX_STRING2 = 3 +SCE_KIX_NUMBER = 4 +SCE_KIX_VAR = 5 +SCE_KIX_MACRO = 6 +SCE_KIX_KEYWORD = 7 +SCE_KIX_FUNCTIONS = 8 +SCE_KIX_OPERATOR = 9 +SCE_KIX_IDENTIFIER = 31 +SCE_GC_DEFAULT = 0 +SCE_GC_COMMENTLINE = 1 +SCE_GC_COMMENTBLOCK = 2 +SCE_GC_GLOBAL = 3 +SCE_GC_EVENT = 4 +SCE_GC_ATTRIBUTE = 5 +SCE_GC_CONTROL = 6 +SCE_GC_COMMAND = 7 +SCE_GC_STRING = 8 +SCE_GC_OPERATOR = 9 +SCE_SN_DEFAULT = 0 +SCE_SN_CODE = 1 +SCE_SN_COMMENTLINE = 2 +SCE_SN_COMMENTLINEBANG = 3 +SCE_SN_NUMBER = 4 +SCE_SN_WORD = 5 +SCE_SN_STRING = 6 +SCE_SN_WORD2 = 7 +SCE_SN_WORD3 = 8 +SCE_SN_PREPROCESSOR = 9 +SCE_SN_OPERATOR = 10 +SCE_SN_IDENTIFIER = 11 +SCE_SN_STRINGEOL = 12 +SCE_SN_REGEXTAG = 13 +SCE_SN_SIGNAL = 14 +SCE_SN_USER = 19 +SCE_AU3_DEFAULT = 0 +SCE_AU3_COMMENT = 1 +SCE_AU3_COMMENTBLOCK = 2 +SCE_AU3_NUMBER = 3 +SCE_AU3_FUNCTION = 4 +SCE_AU3_KEYWORD = 5 +SCE_AU3_MACRO = 6 +SCE_AU3_STRING = 7 +SCE_AU3_OPERATOR = 8 +SCE_AU3_VARIABLE = 9 +SCE_AU3_SENT = 10 +SCE_AU3_PREPROCESSOR = 11 +SCE_AU3_SPECIAL = 12 +SCE_AU3_EXPAND = 13 +SCE_AU3_COMOBJ = 14 +SCE_AU3_UDF = 15 +SCE_APDL_DEFAULT = 0 +SCE_APDL_COMMENT = 1 +SCE_APDL_COMMENTBLOCK = 2 +SCE_APDL_NUMBER = 3 +SCE_APDL_STRING = 4 +SCE_APDL_OPERATOR = 5 +SCE_APDL_WORD = 6 +SCE_APDL_PROCESSOR = 7 +SCE_APDL_COMMAND = 8 +SCE_APDL_SLASHCOMMAND = 9 +SCE_APDL_STARCOMMAND = 10 +SCE_APDL_ARGUMENT = 11 +SCE_APDL_FUNCTION = 12 +SCE_SH_DEFAULT = 0 +SCE_SH_ERROR = 1 +SCE_SH_COMMENTLINE = 2 +SCE_SH_NUMBER = 3 +SCE_SH_WORD = 4 +SCE_SH_STRING = 5 +SCE_SH_CHARACTER = 6 +SCE_SH_OPERATOR = 7 +SCE_SH_IDENTIFIER = 8 +SCE_SH_SCALAR = 9 +SCE_SH_PARAM = 10 +SCE_SH_BACKTICKS = 11 +SCE_SH_HERE_DELIM = 12 +SCE_SH_HERE_Q = 13 +SCE_ASN1_DEFAULT = 0 +SCE_ASN1_COMMENT = 1 +SCE_ASN1_IDENTIFIER = 2 +SCE_ASN1_STRING = 3 +SCE_ASN1_OID = 4 +SCE_ASN1_SCALAR = 5 +SCE_ASN1_KEYWORD = 6 +SCE_ASN1_ATTRIBUTE = 7 +SCE_ASN1_DESCRIPTOR = 8 +SCE_ASN1_TYPE = 9 +SCE_ASN1_OPERATOR = 10 +SCE_VHDL_DEFAULT = 0 +SCE_VHDL_COMMENT = 1 +SCE_VHDL_COMMENTLINEBANG = 2 +SCE_VHDL_NUMBER = 3 +SCE_VHDL_STRING = 4 +SCE_VHDL_OPERATOR = 5 +SCE_VHDL_IDENTIFIER = 6 +SCE_VHDL_STRINGEOL = 7 +SCE_VHDL_KEYWORD = 8 +SCE_VHDL_STDOPERATOR = 9 +SCE_VHDL_ATTRIBUTE = 10 +SCE_VHDL_STDFUNCTION = 11 +SCE_VHDL_STDPACKAGE = 12 +SCE_VHDL_STDTYPE = 13 +SCE_VHDL_USERWORD = 14 +SCE_CAML_DEFAULT = 0 +SCE_CAML_IDENTIFIER = 1 +SCE_CAML_TAGNAME = 2 +SCE_CAML_KEYWORD = 3 +SCE_CAML_KEYWORD2 = 4 +SCE_CAML_KEYWORD3 = 5 +SCE_CAML_LINENUM = 6 +SCE_CAML_OPERATOR = 7 +SCE_CAML_NUMBER = 8 +SCE_CAML_CHAR = 9 +SCE_CAML_STRING = 11 +SCE_CAML_COMMENT = 12 +SCE_CAML_COMMENT1 = 13 +SCE_CAML_COMMENT2 = 14 +SCE_CAML_COMMENT3 = 15 +SCE_HA_DEFAULT = 0 +SCE_HA_IDENTIFIER = 1 +SCE_HA_KEYWORD = 2 +SCE_HA_NUMBER = 3 +SCE_HA_STRING = 4 +SCE_HA_CHARACTER = 5 +SCE_HA_CLASS = 6 +SCE_HA_MODULE = 7 +SCE_HA_CAPITAL = 8 +SCE_HA_DATA = 9 +SCE_HA_IMPORT = 10 +SCE_HA_OPERATOR = 11 +SCE_HA_INSTANCE = 12 +SCE_HA_COMMENTLINE = 13 +SCE_HA_COMMENTBLOCK = 14 +SCE_HA_COMMENTBLOCK2 = 15 +SCE_HA_COMMENTBLOCK3 = 16 +SCE_T3_DEFAULT = 0 +SCE_T3_X_DEFAULT = 1 +SCE_T3_PREPROCESSOR = 2 +SCE_T3_BLOCK_COMMENT = 3 +SCE_T3_LINE_COMMENT = 4 +SCE_T3_OPERATOR = 5 +SCE_T3_KEYWORD = 6 +SCE_T3_NUMBER = 7 +SCE_T3_IDENTIFIER = 8 +SCE_T3_S_STRING = 9 +SCE_T3_D_STRING = 10 +SCE_T3_X_STRING = 11 +SCE_T3_LIB_DIRECTIVE = 12 +SCE_T3_MSG_PARAM = 13 +SCE_T3_HTML_TAG = 14 +SCE_T3_HTML_DEFAULT = 15 +SCE_T3_HTML_STRING = 16 +SCE_T3_USER1 = 17 +SCE_T3_USER2 = 18 +SCE_T3_USER3 = 19 +SCE_T3_BRACE = 20 +SCE_REBOL_DEFAULT = 0 +SCE_REBOL_COMMENTLINE = 1 +SCE_REBOL_COMMENTBLOCK = 2 +SCE_REBOL_PREFACE = 3 +SCE_REBOL_OPERATOR = 4 +SCE_REBOL_CHARACTER = 5 +SCE_REBOL_QUOTEDSTRING = 6 +SCE_REBOL_BRACEDSTRING = 7 +SCE_REBOL_NUMBER = 8 +SCE_REBOL_PAIR = 9 +SCE_REBOL_TUPLE = 10 +SCE_REBOL_BINARY = 11 +SCE_REBOL_MONEY = 12 +SCE_REBOL_ISSUE = 13 +SCE_REBOL_TAG = 14 +SCE_REBOL_FILE = 15 +SCE_REBOL_EMAIL = 16 +SCE_REBOL_URL = 17 +SCE_REBOL_DATE = 18 +SCE_REBOL_TIME = 19 +SCE_REBOL_IDENTIFIER = 20 +SCE_REBOL_WORD = 21 +SCE_REBOL_WORD2 = 22 +SCE_REBOL_WORD3 = 23 +SCE_REBOL_WORD4 = 24 +SCE_REBOL_WORD5 = 25 +SCE_REBOL_WORD6 = 26 +SCE_REBOL_WORD7 = 27 +SCE_REBOL_WORD8 = 28 +SCE_SQL_DEFAULT = 0 +SCE_SQL_COMMENT = 1 +SCE_SQL_COMMENTLINE = 2 +SCE_SQL_COMMENTDOC = 3 +SCE_SQL_NUMBER = 4 +SCE_SQL_WORD = 5 +SCE_SQL_STRING = 6 +SCE_SQL_CHARACTER = 7 +SCE_SQL_SQLPLUS = 8 +SCE_SQL_SQLPLUS_PROMPT = 9 +SCE_SQL_OPERATOR = 10 +SCE_SQL_IDENTIFIER = 11 +SCE_SQL_SQLPLUS_COMMENT = 13 +SCE_SQL_COMMENTLINEDOC = 15 +SCE_SQL_WORD2 = 16 +SCE_SQL_COMMENTDOCKEYWORD = 17 +SCE_SQL_COMMENTDOCKEYWORDERROR = 18 +SCE_SQL_USER1 = 19 +SCE_SQL_USER2 = 20 +SCE_SQL_USER3 = 21 +SCE_SQL_USER4 = 22 +SCE_SQL_QUOTEDIDENTIFIER = 23 +SCE_ST_DEFAULT = 0 +SCE_ST_STRING = 1 +SCE_ST_NUMBER = 2 +SCE_ST_COMMENT = 3 +SCE_ST_SYMBOL = 4 +SCE_ST_BINARY = 5 +SCE_ST_BOOL = 6 +SCE_ST_SELF = 7 +SCE_ST_SUPER = 8 +SCE_ST_NIL = 9 +SCE_ST_GLOBAL = 10 +SCE_ST_RETURN = 11 +SCE_ST_SPECIAL = 12 +SCE_ST_KWSEND = 13 +SCE_ST_ASSIGN = 14 +SCE_ST_CHARACTER = 15 +SCE_ST_SPEC_SEL = 16 +SCE_FS_DEFAULT = 0 +SCE_FS_COMMENT = 1 +SCE_FS_COMMENTLINE = 2 +SCE_FS_COMMENTDOC = 3 +SCE_FS_COMMENTLINEDOC = 4 +SCE_FS_COMMENTDOCKEYWORD = 5 +SCE_FS_COMMENTDOCKEYWORDERROR = 6 +SCE_FS_KEYWORD = 7 +SCE_FS_KEYWORD2 = 8 +SCE_FS_KEYWORD3 = 9 +SCE_FS_KEYWORD4 = 10 +SCE_FS_NUMBER = 11 +SCE_FS_STRING = 12 +SCE_FS_PREPROCESSOR = 13 +SCE_FS_OPERATOR = 14 +SCE_FS_IDENTIFIER = 15 +SCE_FS_DATE = 16 +SCE_FS_STRINGEOL = 17 +SCE_FS_CONSTANT = 18 +SCE_FS_ASM = 19 +SCE_FS_LABEL = 20 +SCE_FS_ERROR = 21 +SCE_FS_HEXNUMBER = 22 +SCE_FS_BINNUMBER = 23 +SCE_CSOUND_DEFAULT = 0 +SCE_CSOUND_COMMENT = 1 +SCE_CSOUND_NUMBER = 2 +SCE_CSOUND_OPERATOR = 3 +SCE_CSOUND_INSTR = 4 +SCE_CSOUND_IDENTIFIER = 5 +SCE_CSOUND_OPCODE = 6 +SCE_CSOUND_HEADERSTMT = 7 +SCE_CSOUND_USERKEYWORD = 8 +SCE_CSOUND_COMMENTBLOCK = 9 +SCE_CSOUND_PARAM = 10 +SCE_CSOUND_ARATE_VAR = 11 +SCE_CSOUND_KRATE_VAR = 12 +SCE_CSOUND_IRATE_VAR = 13 +SCE_CSOUND_GLOBAL_VAR = 14 +SCE_CSOUND_STRINGEOL = 15 +SCE_INNO_DEFAULT = 0 +SCE_INNO_COMMENT = 1 +SCE_INNO_KEYWORD = 2 +SCE_INNO_PARAMETER = 3 +SCE_INNO_SECTION = 4 +SCE_INNO_PREPROC = 5 +SCE_INNO_PREPROC_INLINE = 6 +SCE_INNO_COMMENT_PASCAL = 7 +SCE_INNO_KEYWORD_PASCAL = 8 +SCE_INNO_KEYWORD_USER = 9 +SCE_INNO_STRING_DOUBLE = 10 +SCE_INNO_STRING_SINGLE = 11 +SCE_INNO_IDENTIFIER = 12 +SCE_OPAL_SPACE = 0 +SCE_OPAL_COMMENT_BLOCK = 1 +SCE_OPAL_COMMENT_LINE = 2 +SCE_OPAL_INTEGER = 3 +SCE_OPAL_KEYWORD = 4 +SCE_OPAL_SORT = 5 +SCE_OPAL_STRING = 6 +SCE_OPAL_PAR = 7 +SCE_OPAL_BOOL_CONST = 8 +SCE_OPAL_DEFAULT = 32 +SCE_SPICE_DEFAULT = 0 +SCE_SPICE_IDENTIFIER = 1 +SCE_SPICE_KEYWORD = 2 +SCE_SPICE_KEYWORD2 = 3 +SCE_SPICE_KEYWORD3 = 4 +SCE_SPICE_NUMBER = 5 +SCE_SPICE_DELIMITER = 6 +SCE_SPICE_VALUE = 7 +SCE_SPICE_COMMENTLINE = 8 +SCE_CMAKE_DEFAULT = 0 +SCE_CMAKE_COMMENT = 1 +SCE_CMAKE_STRINGDQ = 2 +SCE_CMAKE_STRINGLQ = 3 +SCE_CMAKE_STRINGRQ = 4 +SCE_CMAKE_COMMANDS = 5 +SCE_CMAKE_PARAMETERS = 6 +SCE_CMAKE_VARIABLE = 7 +SCE_CMAKE_USERDEFINED = 8 +SCE_CMAKE_WHILEDEF = 9 +SCE_CMAKE_FOREACHDEF = 10 +SCE_CMAKE_IFDEFINEDEF = 11 +SCE_CMAKE_MACRODEF = 12 +SCE_CMAKE_STRINGVAR = 13 +SCE_CMAKE_NUMBER = 14 +SCE_GAP_DEFAULT = 0 +SCE_GAP_IDENTIFIER = 1 +SCE_GAP_KEYWORD = 2 +SCE_GAP_KEYWORD2 = 3 +SCE_GAP_KEYWORD3 = 4 +SCE_GAP_KEYWORD4 = 5 +SCE_GAP_STRING = 6 +SCE_GAP_CHAR = 7 +SCE_GAP_OPERATOR = 8 +SCE_GAP_COMMENT = 9 +SCE_GAP_NUMBER = 10 +SCE_GAP_STRINGEOL = 11 +SCE_PLM_DEFAULT = 0 +SCE_PLM_COMMENT = 1 +SCE_PLM_STRING = 2 +SCE_PLM_NUMBER = 3 +SCE_PLM_IDENTIFIER = 4 +SCE_PLM_OPERATOR = 5 +SCE_PLM_CONTROL = 6 +SCE_PLM_KEYWORD = 7 +SCE_4GL_DEFAULT = 0 +SCE_4GL_NUMBER = 1 +SCE_4GL_WORD = 2 +SCE_4GL_STRING = 3 +SCE_4GL_CHARACTER = 4 +SCE_4GL_PREPROCESSOR = 5 +SCE_4GL_OPERATOR = 6 +SCE_4GL_IDENTIFIER = 7 +SCE_4GL_BLOCK = 8 +SCE_4GL_END = 9 +SCE_4GL_COMMENT1 = 10 +SCE_4GL_COMMENT2 = 11 +SCE_4GL_COMMENT3 = 12 +SCE_4GL_COMMENT4 = 13 +SCE_4GL_COMMENT5 = 14 +SCE_4GL_COMMENT6 = 15 +SCE_4GL_DEFAULT_ = 16 +SCE_4GL_NUMBER_ = 17 +SCE_4GL_WORD_ = 18 +SCE_4GL_STRING_ = 19 +SCE_4GL_CHARACTER_ = 20 +SCE_4GL_PREPROCESSOR_ = 21 +SCE_4GL_OPERATOR_ = 22 +SCE_4GL_IDENTIFIER_ = 23 +SCE_4GL_BLOCK_ = 24 +SCE_4GL_END_ = 25 +SCE_4GL_COMMENT1_ = 26 +SCE_4GL_COMMENT2_ = 27 +SCE_4GL_COMMENT3_ = 28 +SCE_4GL_COMMENT4_ = 29 +SCE_4GL_COMMENT5_ = 30 +SCE_4GL_COMMENT6_ = 31 +SCE_ABAQUS_DEFAULT = 0 +SCE_ABAQUS_COMMENT = 1 +SCE_ABAQUS_COMMENTBLOCK = 2 +SCE_ABAQUS_NUMBER = 3 +SCE_ABAQUS_STRING = 4 +SCE_ABAQUS_OPERATOR = 5 +SCE_ABAQUS_WORD = 6 +SCE_ABAQUS_PROCESSOR = 7 +SCE_ABAQUS_COMMAND = 8 +SCE_ABAQUS_SLASHCOMMAND = 9 +SCE_ABAQUS_STARCOMMAND = 10 +SCE_ABAQUS_ARGUMENT = 11 +SCE_ABAQUS_FUNCTION = 12 +SCE_ASY_DEFAULT = 0 +SCE_ASY_COMMENT = 1 +SCE_ASY_COMMENTLINE = 2 +SCE_ASY_NUMBER = 3 +SCE_ASY_WORD = 4 +SCE_ASY_STRING = 5 +SCE_ASY_CHARACTER = 6 +SCE_ASY_OPERATOR = 7 +SCE_ASY_IDENTIFIER = 8 +SCE_ASY_STRINGEOL = 9 +SCE_ASY_COMMENTLINEDOC = 10 +SCE_ASY_WORD2 = 11 +SCE_R_DEFAULT = 0 +SCE_R_COMMENT = 1 +SCE_R_KWORD = 2 +SCE_R_BASEKWORD = 3 +SCE_R_OTHERKWORD = 4 +SCE_R_NUMBER = 5 +SCE_R_STRING = 6 +SCE_R_STRING2 = 7 +SCE_R_OPERATOR = 8 +SCE_R_IDENTIFIER = 9 +SCE_R_INFIX = 10 +SCE_R_INFIXEOL = 11 +SCE_MAGIK_DEFAULT = 0 +SCE_MAGIK_COMMENT = 1 +SCE_MAGIK_HYPER_COMMENT = 16 +SCE_MAGIK_STRING = 2 +SCE_MAGIK_CHARACTER = 3 +SCE_MAGIK_NUMBER = 4 +SCE_MAGIK_IDENTIFIER = 5 +SCE_MAGIK_OPERATOR = 6 +SCE_MAGIK_FLOW = 7 +SCE_MAGIK_CONTAINER = 8 +SCE_MAGIK_BRACKET_BLOCK = 9 +SCE_MAGIK_BRACE_BLOCK = 10 +SCE_MAGIK_SQBRACKET_BLOCK = 11 +SCE_MAGIK_UNKNOWN_KEYWORD = 12 +SCE_MAGIK_KEYWORD = 13 +SCE_MAGIK_PRAGMA = 14 +SCE_MAGIK_SYMBOL = 15 +SCE_POWERSHELL_DEFAULT = 0 +SCE_POWERSHELL_COMMENT = 1 +SCE_POWERSHELL_STRING = 2 +SCE_POWERSHELL_CHARACTER = 3 +SCE_POWERSHELL_NUMBER = 4 +SCE_POWERSHELL_VARIABLE = 5 +SCE_POWERSHELL_OPERATOR = 6 +SCE_POWERSHELL_IDENTIFIER = 7 +SCE_POWERSHELL_KEYWORD = 8 +SCE_POWERSHELL_CMDLET = 9 +SCE_POWERSHELL_ALIAS = 10 +SCE_MYSQL_DEFAULT = 0 +SCE_MYSQL_COMMENT = 1 +SCE_MYSQL_COMMENTLINE = 2 +SCE_MYSQL_VARIABLE = 3 +SCE_MYSQL_SYSTEMVARIABLE = 4 +SCE_MYSQL_KNOWNSYSTEMVARIABLE = 5 +SCE_MYSQL_NUMBER = 6 +SCE_MYSQL_MAJORKEYWORD = 7 +SCE_MYSQL_KEYWORD = 8 +SCE_MYSQL_DATABASEOBJECT = 9 +SCE_MYSQL_PROCEDUREKEYWORD = 10 +SCE_MYSQL_STRING = 11 +SCE_MYSQL_SQSTRING = 12 +SCE_MYSQL_DQSTRING = 13 +SCE_MYSQL_OPERATOR = 14 +SCE_MYSQL_FUNCTION = 15 +SCE_MYSQL_IDENTIFIER = 16 +SCE_MYSQL_QUOTEDIDENTIFIER = 17 +SCE_MYSQL_USER1 = 18 +SCE_MYSQL_USER2 = 19 +SCE_MYSQL_USER3 = 20 +SCE_PO_DEFAULT = 0 +SCE_PO_COMMENT = 1 +SCE_PO_MSGID = 2 +SCE_PO_MSGID_TEXT = 3 +SCE_PO_MSGSTR = 4 +SCE_PO_MSGSTR_TEXT = 5 +SCE_PO_MSGCTXT = 6 +SCE_PO_MSGCTXT_TEXT = 7 +SCE_PO_FUZZY = 8 +SCLEX_ASP = 29 +SCLEX_PHP = 30 diff --git a/myenv/Lib/site-packages/pythonwin/pywin/scintilla/view.py b/myenv/Lib/site-packages/pythonwin/pywin/scintilla/view.py new file mode 100644 index 000000000..3783666fc --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/scintilla/view.py @@ -0,0 +1,832 @@ +# A general purpose MFC CCtrlView view that uses Scintilla. + +import array +import os +import re +import string +import struct +import sys + +import __main__ # for attribute lookup +import afxres +import win32con +import win32ui +from pywin.mfc import dialog, docview + +from . import IDLEenvironment # IDLE emulation. +from . import bindings, control, keycodes, scintillacon + +PRINTDLGORD = 1538 +IDC_PRINT_MAG_EDIT = 1010 +EM_FORMATRANGE = win32con.WM_USER + 57 + +wordbreaks = "._" + string.ascii_uppercase + string.ascii_lowercase + string.digits + +patImport = re.compile("import (?P.*)") + +_event_commands = [ + # File menu + "win32ui.ID_FILE_LOCATE", + "win32ui.ID_FILE_CHECK", + "afxres.ID_FILE_CLOSE", + "afxres.ID_FILE_NEW", + "afxres.ID_FILE_OPEN", + "afxres.ID_FILE_SAVE", + "afxres.ID_FILE_SAVE_AS", + "win32ui.ID_FILE_SAVE_ALL", + # Edit menu + "afxres.ID_EDIT_UNDO", + "afxres.ID_EDIT_REDO", + "afxres.ID_EDIT_CUT", + "afxres.ID_EDIT_COPY", + "afxres.ID_EDIT_PASTE", + "afxres.ID_EDIT_SELECT_ALL", + "afxres.ID_EDIT_FIND", + "afxres.ID_EDIT_REPEAT", + "afxres.ID_EDIT_REPLACE", + # View menu + "win32ui.ID_VIEW_WHITESPACE", + "win32ui.ID_VIEW_FIXED_FONT", + "win32ui.ID_VIEW_BROWSE", + "win32ui.ID_VIEW_INTERACTIVE", + # Window menu + "afxres.ID_WINDOW_ARRANGE", + "afxres.ID_WINDOW_CASCADE", + "afxres.ID_WINDOW_NEW", + "afxres.ID_WINDOW_SPLIT", + "afxres.ID_WINDOW_TILE_HORZ", + "afxres.ID_WINDOW_TILE_VERT", + # Others + "afxres.ID_APP_EXIT", + "afxres.ID_APP_ABOUT", +] + +_extra_event_commands = [ + ("EditDelete", afxres.ID_EDIT_CLEAR), + ("LocateModule", win32ui.ID_FILE_LOCATE), + ("GotoLine", win32ui.ID_EDIT_GOTO_LINE), + ("DbgBreakpointToggle", win32ui.IDC_DBG_ADD), + ("DbgGo", win32ui.IDC_DBG_GO), + ("DbgStepOver", win32ui.IDC_DBG_STEPOVER), + ("DbgStep", win32ui.IDC_DBG_STEP), + ("DbgStepOut", win32ui.IDC_DBG_STEPOUT), + ("DbgBreakpointClearAll", win32ui.IDC_DBG_CLEAR), + ("DbgClose", win32ui.IDC_DBG_CLOSE), +] + +event_commands = [] + + +def _CreateEvents(): + for name in _event_commands: + val = eval(name) + name_parts = name.split("_")[1:] + name_parts = [p.capitalize() for p in name_parts] + event = "".join(name_parts) + event_commands.append((event, val)) + for name, id in _extra_event_commands: + event_commands.append((name, id)) + + +_CreateEvents() +del _event_commands +del _extra_event_commands + +command_reflectors = [ + (win32ui.ID_EDIT_UNDO, win32con.WM_UNDO), + (win32ui.ID_EDIT_REDO, scintillacon.SCI_REDO), + (win32ui.ID_EDIT_CUT, win32con.WM_CUT), + (win32ui.ID_EDIT_COPY, win32con.WM_COPY), + (win32ui.ID_EDIT_PASTE, win32con.WM_PASTE), + (win32ui.ID_EDIT_CLEAR, win32con.WM_CLEAR), + (win32ui.ID_EDIT_SELECT_ALL, scintillacon.SCI_SELECTALL), +] + + +def DoBraceMatch(control): + curPos = control.SCIGetCurrentPos() + charBefore = " " + if curPos: + charBefore = control.SCIGetCharAt(curPos - 1) + charAt = control.SCIGetCharAt(curPos) + braceAtPos = braceOpposite = -1 + if charBefore in "[](){}": + braceAtPos = curPos - 1 + if braceAtPos == -1: + if charAt in "[](){}": + braceAtPos = curPos + if braceAtPos != -1: + braceOpposite = control.SCIBraceMatch(braceAtPos, 0) + if braceAtPos != -1 and braceOpposite == -1: + control.SCIBraceBadHighlight(braceAtPos) + else: + # either clear them both or set them both. + control.SCIBraceHighlight(braceAtPos, braceOpposite) + + +def _get_class_attributes(ob): + # Recurse into base classes looking for attributes + items = [] + try: + items = items + dir(ob) + for i in ob.__bases__: + for item in _get_class_attributes(i): + if item not in items: + items.append(item) + except AttributeError: + pass + return items + + +# Supposed to look like an MFC CEditView, but +# also supports IDLE extensions and other source code generic features. +class CScintillaView(docview.CtrlView, control.CScintillaColorEditInterface): + def __init__(self, doc): + docview.CtrlView.__init__( + self, + doc, + "Scintilla", + win32con.WS_CHILD + | win32con.WS_VSCROLL + | win32con.WS_HSCROLL + | win32con.WS_CLIPCHILDREN + | win32con.WS_VISIBLE, + ) + self._tabWidth = ( + 8 # Mirror of what we send to Scintilla - never change this directly + ) + self.bAutoCompleteAttributes = 1 + self.bShowCallTips = 1 + self.bMatchBraces = 0 # Editor option will default this to true later! + self.bindings = bindings.BindingsManager(self) + + self.idle = IDLEenvironment.IDLEEditorWindow(self) + self.idle.IDLEExtension("AutoExpand") + # SendScintilla is called so frequently it is worth optimizing. + self.SendScintilla = self._obj_.SendMessage + + def _MakeColorizer(self): + ext = os.path.splitext(self.GetDocument().GetPathName())[1] + from . import formatter + + return formatter.BuiltinPythonSourceFormatter(self, ext) + + # def SendScintilla(self, msg, w=0, l=0): + # return self._obj_.SendMessage(msg, w, l) + + def SCISetTabWidth(self, width): + # I need to remember the tab-width for the AutoIndent extension. This may go. + self._tabWidth = width + control.CScintillaEditInterface.SCISetTabWidth(self, width) + + def GetTabWidth(self): + return self._tabWidth + + def HookHandlers(self): + # Create events for all the menu names. + for name, val in event_commands: + # handler = lambda id, code, tosend=val, parent=parent: parent.OnCommand(tosend, 0) and 0 + self.bindings.bind(name, None, cid=val) + + # Hook commands that do nothing other than send Scintilla messages. + for command, reflection in command_reflectors: + handler = ( + lambda id, code, ss=self.SendScintilla, tosend=reflection: ss(tosend) + and 0 + ) + self.HookCommand(handler, command) + + self.HookCommand(self.OnCmdViewWS, win32ui.ID_VIEW_WHITESPACE) + self.HookCommandUpdate(self.OnUpdateViewWS, win32ui.ID_VIEW_WHITESPACE) + self.HookCommand( + self.OnCmdViewIndentationGuides, win32ui.ID_VIEW_INDENTATIONGUIDES + ) + self.HookCommandUpdate( + self.OnUpdateViewIndentationGuides, win32ui.ID_VIEW_INDENTATIONGUIDES + ) + self.HookCommand(self.OnCmdViewRightEdge, win32ui.ID_VIEW_RIGHT_EDGE) + self.HookCommandUpdate(self.OnUpdateViewRightEdge, win32ui.ID_VIEW_RIGHT_EDGE) + self.HookCommand(self.OnCmdViewEOL, win32ui.ID_VIEW_EOL) + self.HookCommandUpdate(self.OnUpdateViewEOL, win32ui.ID_VIEW_EOL) + self.HookCommand(self.OnCmdViewFixedFont, win32ui.ID_VIEW_FIXED_FONT) + self.HookCommandUpdate(self.OnUpdateViewFixedFont, win32ui.ID_VIEW_FIXED_FONT) + self.HookCommand(self.OnCmdFileLocate, win32ui.ID_FILE_LOCATE) + self.HookCommand(self.OnCmdEditFind, win32ui.ID_EDIT_FIND) + self.HookCommand(self.OnCmdEditRepeat, win32ui.ID_EDIT_REPEAT) + self.HookCommand(self.OnCmdEditReplace, win32ui.ID_EDIT_REPLACE) + self.HookCommand(self.OnCmdGotoLine, win32ui.ID_EDIT_GOTO_LINE) + self.HookCommand(self.OnFilePrint, afxres.ID_FILE_PRINT) + self.HookCommand(self.OnFilePrint, afxres.ID_FILE_PRINT_DIRECT) + self.HookCommand(self.OnFilePrintPreview, win32ui.ID_FILE_PRINT_PREVIEW) + # Key bindings. + self.HookMessage(self.OnKeyDown, win32con.WM_KEYDOWN) + self.HookMessage(self.OnKeyDown, win32con.WM_SYSKEYDOWN) + # Hook wheeley mouse events + # self.HookMessage(self.OnMouseWheel, win32con.WM_MOUSEWHEEL) + self.HookFormatter() + + def OnInitialUpdate(self): + doc = self.GetDocument() + + # Enable Unicode + self.SendScintilla(scintillacon.SCI_SETCODEPAGE, scintillacon.SC_CP_UTF8, 0) + self.SendScintilla(scintillacon.SCI_SETKEYSUNICODE, 1, 0) + + # Create margins + self.SendScintilla( + scintillacon.SCI_SETMARGINTYPEN, 1, scintillacon.SC_MARGIN_SYMBOL + ) + self.SendScintilla(scintillacon.SCI_SETMARGINMASKN, 1, 0xF) + self.SendScintilla( + scintillacon.SCI_SETMARGINTYPEN, 2, scintillacon.SC_MARGIN_SYMBOL + ) + self.SendScintilla( + scintillacon.SCI_SETMARGINMASKN, 2, scintillacon.SC_MASK_FOLDERS + ) + self.SendScintilla(scintillacon.SCI_SETMARGINSENSITIVEN, 2, 1) + + self.GetDocument().HookViewNotifications( + self + ) # is there an MFC way to grab this? + self.HookHandlers() + + # Load the configuration information. + self.OnWinIniChange(None) + + self.SetSel() + + self.GetDocument().FinalizeViewCreation( + self + ) # is there an MFC way to grab this? + + def _GetSubConfigNames(self): + return None # By default we use only sections without sub-sections. + + def OnWinIniChange(self, section=None): + self.bindings.prepare_configure() + try: + self.DoConfigChange() + finally: + self.bindings.complete_configure() + + def DoConfigChange(self): + # Bit of a hack I dont kow what to do about - these should be "editor options" + from pywin.framework.editor import GetEditorOption + + self.bAutoCompleteAttributes = GetEditorOption("Autocomplete Attributes", 1) + self.bShowCallTips = GetEditorOption("Show Call Tips", 1) + # Update the key map and extension data. + configManager.configure(self, self._GetSubConfigNames()) + if configManager.last_error: + win32ui.MessageBox(configManager.last_error, "Configuration Error") + self.bMatchBraces = GetEditorOption("Match Braces", 1) + self.ApplyFormattingStyles(1) + + def OnDestroy(self, msg): + self.bindings.close() + self.bindings = None + self.idle.close() + self.idle = None + control.CScintillaColorEditInterface.close(self) + return docview.CtrlView.OnDestroy(self, msg) + + def OnMouseWheel(self, msg): + zDelta = msg[2] >> 16 + vpos = self.GetScrollPos(win32con.SB_VERT) + vpos = vpos - zDelta / 40 # 3 lines per notch + self.SetScrollPos(win32con.SB_VERT, vpos) + self.SendScintilla( + win32con.WM_VSCROLL, (vpos << 16) | win32con.SB_THUMBPOSITION, 0 + ) + + def OnBraceMatch(self, std, extra): + if not self.bMatchBraces: + return + DoBraceMatch(self) + + def OnNeedShown(self, std, extra): + notify = self.SCIUnpackNotifyMessage(extra) + # OnNeedShown is called before an edit operation when + # text is folded (as it is possible the text insertion will happen + # in a folded region.) As this happens _before_ the insert, + # we ignore the length (if we are at EOF, pos + length may + # actually be beyond the end of buffer) + self.EnsureCharsVisible(notify.position) + + def EnsureCharsVisible(self, start, end=None): + if end is None: + end = start + lineStart = self.LineFromChar(min(start, end)) + lineEnd = self.LineFromChar(max(start, end)) + while lineStart <= lineEnd: + self.SCIEnsureVisible(lineStart) + lineStart = lineStart + 1 + + # Helper to add an event to a menu. + def AppendMenu(self, menu, text="", event=None, flags=None, checked=0): + if event is None: + assert flags is not None, "No event or custom flags!" + cmdid = 0 + else: + cmdid = self.bindings.get_command_id(event) + if cmdid is None: + # No event of that name - no point displaying it. + print( + 'View.AppendMenu(): Unknown event "%s" specified for menu text "%s" - ignored' + % (event, text) + ) + return + keyname = configManager.get_key_binding(event, self._GetSubConfigNames()) + if keyname is not None: + text = text + "\t" + keyname + if flags is None: + flags = win32con.MF_STRING | win32con.MF_ENABLED + if checked: + flags = flags | win32con.MF_CHECKED + menu.AppendMenu(flags, cmdid, text) + + def OnKeyDown(self, msg): + return self.bindings.fire_key_event(msg) + + def GotoEndOfFileEvent(self, event): + self.SetSel(-1) + + def KeyDotEvent(self, event): + ## Don't trigger autocomplete if any text is selected + s, e = self.GetSel() + if s != e: + return 1 + self.SCIAddText(".") + if self.bAutoCompleteAttributes: + self._AutoComplete() + + # View Whitespace/EOL/Indentation UI. + + def OnCmdViewWS(self, cmd, code): # Handle the menu command + viewWS = self.SCIGetViewWS() + self.SCISetViewWS(not viewWS) + + def OnUpdateViewWS(self, cmdui): # Update the tick on the UI. + cmdui.SetCheck(self.SCIGetViewWS()) + cmdui.Enable() + + def OnCmdViewIndentationGuides(self, cmd, code): # Handle the menu command + viewIG = self.SCIGetIndentationGuides() + self.SCISetIndentationGuides(not viewIG) + + def OnUpdateViewIndentationGuides(self, cmdui): # Update the tick on the UI. + cmdui.SetCheck(self.SCIGetIndentationGuides()) + cmdui.Enable() + + def OnCmdViewRightEdge(self, cmd, code): # Handle the menu command + if self.SCIGetEdgeMode() == scintillacon.EDGE_NONE: + mode = scintillacon.EDGE_BACKGROUND + else: + mode = scintillacon.EDGE_NONE + self.SCISetEdgeMode(mode) + + def OnUpdateViewRightEdge(self, cmdui): # Update the tick on the UI. + cmdui.SetCheck(self.SCIGetEdgeMode() != scintillacon.EDGE_NONE) + cmdui.Enable() + + def OnCmdViewEOL(self, cmd, code): # Handle the menu command + viewEOL = self.SCIGetViewEOL() + self.SCISetViewEOL(not viewEOL) + + def OnUpdateViewEOL(self, cmdui): # Update the tick on the UI. + cmdui.SetCheck(self.SCIGetViewEOL()) + cmdui.Enable() + + def OnCmdViewFixedFont(self, cmd, code): # Handle the menu command + self._GetColorizer().bUseFixed = not self._GetColorizer().bUseFixed + self.ApplyFormattingStyles(0) + # Ensure the selection is visible! + self.ScrollCaret() + + def OnUpdateViewFixedFont(self, cmdui): # Update the tick on the UI. + c = self._GetColorizer() + if c is not None: + cmdui.SetCheck(c.bUseFixed) + cmdui.Enable(c is not None) + + def OnCmdEditFind(self, cmd, code): + from . import find + + find.ShowFindDialog() + + def OnCmdEditRepeat(self, cmd, code): + from . import find + + find.FindNext() + + def OnCmdEditReplace(self, cmd, code): + from . import find + + find.ShowReplaceDialog() + + def OnCmdFileLocate(self, cmd, id): + line = self.GetLine().strip() + import pywin.framework.scriptutils + + m = patImport.match(line) + if m: + # Module name on this line - locate that! + modName = m.group("name") + fileName = pywin.framework.scriptutils.LocatePythonFile(modName) + if fileName is None: + win32ui.SetStatusText("Can't locate module %s" % modName) + return 1 # Let the default get it. + else: + win32ui.GetApp().OpenDocumentFile(fileName) + else: + # Just to a "normal" locate - let the default handler get it. + return 1 + return 0 + + def OnCmdGotoLine(self, cmd, id): + try: + lineNo = int(input("Enter Line Number")) - 1 + except (ValueError, KeyboardInterrupt): + return 0 + self.SCIEnsureVisible(lineNo) + self.SCIGotoLine(lineNo) + return 0 + + def SaveTextFile(self, filename, encoding=None): + doc = self.GetDocument() + doc._SaveTextToFile(self, filename, encoding=encoding) + doc.SetModifiedFlag(0) + return 1 + + def _AutoComplete(self): + def list2dict(l): + ret = {} + for i in l: + ret[i] = None + return ret + + self.SCIAutoCCancel() # Cancel old auto-complete lists. + # First try and get an object without evaluating calls + ob = self._GetObjectAtPos(bAllowCalls=0) + # If that failed, try and process call or indexing to get the object. + if ob is None: + ob = self._GetObjectAtPos(bAllowCalls=1) + items_dict = {} + if ob is not None: + try: # Catch unexpected errors when fetching attribute names from the object + # extra attributes of win32ui objects + if hasattr(ob, "_obj_"): + try: + items_dict.update(list2dict(dir(ob._obj_))) + except AttributeError: + pass # object has no __dict__ + + # normal attributes + try: + items_dict.update(list2dict(dir(ob))) + except AttributeError: + pass # object has no __dict__ + if hasattr(ob, "__class__"): + items_dict.update(list2dict(_get_class_attributes(ob.__class__))) + # The object may be a COM object with typelib support - lets see if we can get its props. + # (contributed by Stefan Migowsky) + try: + # Get the automation attributes + items_dict.update(ob.__class__._prop_map_get_) + # See if there is an write only property + # could be optimized + items_dict.update(ob.__class__._prop_map_put_) + # append to the already evaluated list + except AttributeError: + pass + # The object might be a pure COM dynamic dispatch with typelib support - lets see if we can get its props. + if hasattr(ob, "_oleobj_"): + try: + for iTI in range(0, ob._oleobj_.GetTypeInfoCount()): + typeInfo = ob._oleobj_.GetTypeInfo(iTI) + self._UpdateWithITypeInfo(items_dict, typeInfo) + except: + pass + except: + win32ui.SetStatusText( + "Error attempting to get object attributes - %s" + % (repr(sys.exc_info()[0]),) + ) + + # ensure all keys are strings. + items = [str(k) for k in items_dict.keys()] + # All names that start with "_" go! + items = [k for k in items if not k.startswith("_")] + + if not items: + # Heuristics a-la AutoExpand + # The idea is to find other usages of the current binding + # and assume, that it refers to the same object (or at least, + # to an object of the same type) + # Contributed by Vadim Chugunov [vadimch@yahoo.com] + left, right = self._GetWordSplit() + if left == "": # Ignore standalone dots + return None + # We limit our search to the current class, if that + # information is available + minline, maxline, curclass = self._GetClassInfoFromBrowser() + endpos = self.LineIndex(maxline) + text = self.GetTextRange(self.LineIndex(minline), endpos) + try: + l = re.findall(r"\b" + left + "\.\w+", text) + except re.error: + # parens etc may make an invalid RE, but this code wouldnt + # benefit even if the RE did work :-) + l = [] + prefix = len(left) + 1 + unique = {} + for li in l: + unique[li[prefix:]] = 1 + # Assuming traditional usage of self... + if curclass and left == "self": + self._UpdateWithClassMethods(unique, curclass) + + items = [ + word for word in unique.keys() if word[:2] != "__" or word[-2:] != "__" + ] + # Ignore the word currently to the right of the dot - probably a red-herring. + try: + items.remove(right[1:]) + except ValueError: + pass + if items: + items.sort() + self.SCIAutoCSetAutoHide(0) + self.SCIAutoCShow(items) + + def _UpdateWithITypeInfo(self, items_dict, typeInfo): + import pythoncom + + typeInfos = [typeInfo] + # suppress IDispatch and IUnknown methods + inspectedIIDs = {pythoncom.IID_IDispatch: None} + + while len(typeInfos) > 0: + typeInfo = typeInfos.pop() + typeAttr = typeInfo.GetTypeAttr() + + if typeAttr.iid not in inspectedIIDs: + inspectedIIDs[typeAttr.iid] = None + for iFun in range(0, typeAttr.cFuncs): + funDesc = typeInfo.GetFuncDesc(iFun) + funName = typeInfo.GetNames(funDesc.memid)[0] + if funName not in items_dict: + items_dict[funName] = None + + # Inspect the type info of all implemented types + # E.g. IShellDispatch5 implements IShellDispatch4 which implements IShellDispatch3 ... + for iImplType in range(0, typeAttr.cImplTypes): + iRefType = typeInfo.GetRefTypeOfImplType(iImplType) + refTypeInfo = typeInfo.GetRefTypeInfo(iRefType) + typeInfos.append(refTypeInfo) + + # TODO: This is kinda slow. Probably need some kind of cache + # here that is flushed upon file save + # Or maybe we don't need the superclass methods at all ? + def _UpdateWithClassMethods(self, dict, classinfo): + if not hasattr(classinfo, "methods"): + # No 'methods' - probably not what we think it is. + return + dict.update(classinfo.methods) + for super in classinfo.super: + if hasattr(super, "methods"): + self._UpdateWithClassMethods(dict, super) + + # Find which class definition caret is currently in and return + # indexes of the the first and the last lines of that class definition + # Data is obtained from module browser (if enabled) + def _GetClassInfoFromBrowser(self, pos=-1): + minline = 0 + maxline = self.GetLineCount() - 1 + doc = self.GetParentFrame().GetActiveDocument() + browser = None + try: + if doc is not None: + browser = doc.GetAllViews()[1] + except IndexError: + pass + if browser is None: + return (minline, maxline, None) # Current window has no browser + if not browser.list: + return (minline, maxline, None) # Not initialized + path = self.GetDocument().GetPathName() + if not path: + return (minline, maxline, None) # No current path + + import pywin.framework.scriptutils + + curmodule, path = pywin.framework.scriptutils.GetPackageModuleName(path) + try: + clbrdata = browser.list.root.clbrdata + except AttributeError: + return (minline, maxline, None) # No class data for this module. + curline = self.LineFromChar(pos) + curclass = None + # Find out which class we are in + for item in clbrdata.values(): + if item.module == curmodule: + item_lineno = ( + item.lineno - 1 + ) # Scintilla counts lines from 0, whereas pyclbr - from 1 + if minline < item_lineno <= curline: + minline = item_lineno + curclass = item + if curline < item_lineno < maxline: + maxline = item_lineno + return (minline, maxline, curclass) + + def _GetObjectAtPos(self, pos=-1, bAllowCalls=0): + left, right = self._GetWordSplit(pos, bAllowCalls) + if left: # It is an attribute lookup + # How is this for a hack! + namespace = sys.modules.copy() + namespace.update(__main__.__dict__) + # Get the debugger's context. + try: + from pywin.framework import interact + + if interact.edit is not None and interact.edit.currentView is not None: + globs, locs = interact.edit.currentView.GetContext()[:2] + if globs: + namespace.update(globs) + if locs: + namespace.update(locs) + except ImportError: + pass + try: + return eval(left, namespace) + except: + pass + return None + + def _GetWordSplit(self, pos=-1, bAllowCalls=0): + if pos == -1: + pos = self.GetSel()[0] - 1 # Character before current one + limit = self.GetTextLength() + before = [] + after = [] + index = pos - 1 + wordbreaks_use = wordbreaks + if bAllowCalls: + wordbreaks_use = wordbreaks_use + "()[]" + while index >= 0: + char = self.SCIGetCharAt(index) + if char not in wordbreaks_use: + break + before.insert(0, char) + index = index - 1 + index = pos + while index <= limit: + char = self.SCIGetCharAt(index) + if char not in wordbreaks_use: + break + after.append(char) + index = index + 1 + return "".join(before), "".join(after) + + def OnPrepareDC(self, dc, pInfo): + # print "OnPrepareDC for page", pInfo.GetCurPage(), "of", pInfo.GetFromPage(), "to", pInfo.GetToPage(), ", starts=", self.starts + if dc.IsPrinting(): + # Check if we are beyond the end. + # (only do this when actually printing, else messes up print preview!) + if not pInfo.GetPreview() and self.starts is not None: + prevPage = pInfo.GetCurPage() - 1 + if prevPage > 0 and self.starts[prevPage] >= self.GetTextLength(): + # All finished. + pInfo.SetContinuePrinting(0) + return + dc.SetMapMode(win32con.MM_TEXT) + + def OnPreparePrinting(self, pInfo): + flags = ( + win32ui.PD_USEDEVMODECOPIES | win32ui.PD_ALLPAGES | win32ui.PD_NOSELECTION + ) # Dont support printing just a selection. + # NOTE: Custom print dialogs are stopping the user's values from coming back :-( + # self.prtDlg = PrintDialog(pInfo, PRINTDLGORD, flags) + # pInfo.SetPrintDialog(self.prtDlg) + pInfo.SetMinPage(1) + # max page remains undefined for now. + pInfo.SetFromPage(1) + pInfo.SetToPage(1) + ret = self.DoPreparePrinting(pInfo) + return ret + + def OnBeginPrinting(self, dc, pInfo): + self.starts = None + return self._obj_.OnBeginPrinting(dc, pInfo) + + def CalculatePageRanges(self, dc, pInfo): + # Calculate page ranges and max page + self.starts = {0: 0} + metrics = dc.GetTextMetrics() + left, top, right, bottom = pInfo.GetDraw() + # Leave space at the top for the header. + rc = (left, top + int((9 * metrics["tmHeight"]) / 2), right, bottom) + pageStart = 0 + maxPage = 0 + textLen = self.GetTextLength() + while pageStart < textLen: + pageStart = self.FormatRange(dc, pageStart, textLen, rc, 0) + maxPage = maxPage + 1 + self.starts[maxPage] = pageStart + # And a sentinal for one page past the end + self.starts[maxPage + 1] = textLen + # When actually printing, maxPage doesnt have any effect at this late state. + # but is needed to make the Print Preview work correctly. + pInfo.SetMaxPage(maxPage) + + def OnFilePrintPreview(self, *arg): + self._obj_.OnFilePrintPreview() + + def OnFilePrint(self, *arg): + self._obj_.OnFilePrint() + + def FormatRange(self, dc, pageStart, lengthDoc, rc, draw): + """ + typedef struct _formatrange { + HDC hdc; + HDC hdcTarget; + RECT rc; + RECT rcPage; + CHARRANGE chrg;} FORMATRANGE; + """ + fmt = "PPIIIIIIIIll" + hdcRender = dc.GetHandleOutput() + hdcFormat = dc.GetHandleAttrib() + fr = struct.pack( + fmt, + hdcRender, + hdcFormat, + rc[0], + rc[1], + rc[2], + rc[3], + rc[0], + rc[1], + rc[2], + rc[3], + pageStart, + lengthDoc, + ) + nextPageStart = self.SendScintilla(EM_FORMATRANGE, draw, fr) + return nextPageStart + + def OnPrint(self, dc, pInfo): + metrics = dc.GetTextMetrics() + # print "dev", w, h, l, metrics['tmAscent'], metrics['tmDescent'] + if self.starts is None: + self.CalculatePageRanges(dc, pInfo) + pageNum = pInfo.GetCurPage() - 1 + # Setup the header of the page - docname on left, pagenum on right. + doc = self.GetDocument() + cxChar = metrics["tmAveCharWidth"] + cyChar = metrics["tmHeight"] + left, top, right, bottom = pInfo.GetDraw() + dc.TextOut(0, 2 * cyChar, doc.GetTitle()) + pagenum_str = win32ui.LoadString(afxres.AFX_IDS_PRINTPAGENUM) % (pageNum + 1,) + dc.SetTextAlign(win32con.TA_RIGHT) + dc.TextOut(right, 2 * cyChar, pagenum_str) + dc.SetTextAlign(win32con.TA_LEFT) + top = top + int((7 * cyChar) / 2) + dc.MoveTo(left, top) + dc.LineTo(right, top) + top = top + cyChar + rc = (left, top, right, bottom) + nextPageStart = self.FormatRange( + dc, self.starts[pageNum], self.starts[pageNum + 1], rc, 1 + ) + + +def LoadConfiguration(): + global configManager + # Bit of a hack I dont kow what to do about? + from .config import ConfigManager + + configName = rc = win32ui.GetProfileVal("Editor", "Keyboard Config", "default") + configManager = ConfigManager(configName) + if configManager.last_error: + bTryDefault = 0 + msg = "Error loading configuration '%s'\n\n%s" % ( + configName, + configManager.last_error, + ) + if configName != "default": + msg = msg + "\n\nThe default configuration will be loaded." + bTryDefault = 1 + win32ui.MessageBox(msg) + if bTryDefault: + configManager = ConfigManager("default") + if configManager.last_error: + win32ui.MessageBox( + "Error loading configuration 'default'\n\n%s" + % (configManager.last_error) + ) + + +configManager = None +LoadConfiguration() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/tools/TraceCollector.py b/myenv/Lib/site-packages/pythonwin/pywin/tools/TraceCollector.py new file mode 100644 index 000000000..5800488b9 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/tools/TraceCollector.py @@ -0,0 +1,79 @@ +# win32traceutil like utility for Pythonwin +import _thread + +import win32api +import win32event +import win32trace +from pywin.framework import winout + +outputWindow = None + + +def CollectorThread(stopEvent, file): + win32trace.InitRead() + handle = win32trace.GetHandle() + # Run this thread at a lower priority to the main message-loop (and printing output) + # thread can keep up + import win32process + + win32process.SetThreadPriority( + win32api.GetCurrentThread(), win32process.THREAD_PRIORITY_BELOW_NORMAL + ) + + try: + while 1: + rc = win32event.WaitForMultipleObjects( + (handle, stopEvent), 0, win32event.INFINITE + ) + if rc == win32event.WAIT_OBJECT_0: + # About the only char we can't live with is \0! + file.write(win32trace.read().replace("\0", "")) + else: + # Stop event + break + finally: + win32trace.TermRead() + print("Thread dieing") + + +class WindowOutput(winout.WindowOutput): + def __init__(self, *args): + winout.WindowOutput.__init__(*(self,) + args) + self.hStopThread = win32event.CreateEvent(None, 0, 0, None) + _thread.start_new(CollectorThread, (self.hStopThread, self)) + + def _StopThread(self): + win32event.SetEvent(self.hStopThread) + self.hStopThread = None + + def Close(self): + self._StopThread() + winout.WindowOutput.Close(self) + # def OnViewDestroy(self, frame): + # return winout.WindowOutput.OnViewDestroy(self, frame) + # def Create(self, title=None, style = None): + # rc = winout.WindowOutput.Create(self, title, style) + return rc + + +def MakeOutputWindow(): + # Note that it will not show until the first string written or + # you pass bShow = 1 + global outputWindow + if outputWindow is None: + title = "Python Trace Collector" + # queueingFlag doesnt matter, as all output will come from new thread + outputWindow = WindowOutput(title, title) + # Let people know what this does! + msg = """\ +# This window will display output from any programs that import win32traceutil +# win32com servers registered with '--debug' are in this category. +""" + outputWindow.write(msg) + # force existing window open + outputWindow.write("") + return outputWindow + + +if __name__ == "__main__": + MakeOutputWindow() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/tools/__init__.py b/myenv/Lib/site-packages/pythonwin/pywin/tools/__init__.py new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/tools/__init__.py @@ -0,0 +1 @@ + diff --git a/myenv/Lib/site-packages/pythonwin/pywin/tools/browseProjects.py b/myenv/Lib/site-packages/pythonwin/pywin/tools/browseProjects.py new file mode 100644 index 000000000..e3255579a --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/tools/browseProjects.py @@ -0,0 +1,324 @@ +import glob +import os +import pyclbr + +import afxres +import commctrl +import pywin.framework.scriptutils +import regutil +import win32api +import win32con +import win32ui +from pywin.mfc import dialog + +from . import hierlist + + +class HLIErrorItem(hierlist.HierListItem): + def __init__(self, text): + self.text = text + hierlist.HierListItem.__init__(self) + + def GetText(self): + return self.text + + +class HLICLBRItem(hierlist.HierListItem): + def __init__(self, name, file, lineno, suffix=""): + # If the 'name' object itself has a .name, use it. Not sure + # how this happens, but seems pyclbr related. + # See PyWin32 bug 817035 + self.name = getattr(name, "name", name) + self.file = file + self.lineno = lineno + self.suffix = suffix + + def __lt__(self, other): + return self.name < other.name + + def __eq__(self, other): + return self.name == other.name + + def GetText(self): + return self.name + self.suffix + + def TakeDefaultAction(self): + if self.file: + pywin.framework.scriptutils.JumpToDocument( + self.file, self.lineno, bScrollToTop=1 + ) + else: + win32ui.SetStatusText("The source of this object is unknown") + + def PerformItemSelected(self): + if self.file is None: + msg = "%s - source can not be located." % (self.name,) + else: + msg = "%s defined at line %d of %s" % (self.name, self.lineno, self.file) + win32ui.SetStatusText(msg) + + +class HLICLBRClass(HLICLBRItem): + def __init__(self, clbrclass, suffix=""): + try: + name = clbrclass.name + file = clbrclass.file + lineno = clbrclass.lineno + self.super = clbrclass.super + self.methods = clbrclass.methods + except AttributeError: + name = clbrclass + file = lineno = None + self.super = [] + self.methods = {} + HLICLBRItem.__init__(self, name, file, lineno, suffix) + + def GetSubList(self): + ret = [] + for c in self.super: + ret.append(HLICLBRClass(c, " (Parent class)")) + for meth, lineno in self.methods.items(): + ret.append(HLICLBRMethod(meth, self.file, lineno, " (method)")) + return ret + + def IsExpandable(self): + return len(self.methods) + len(self.super) + + def GetBitmapColumn(self): + return 21 + + +class HLICLBRFunction(HLICLBRClass): + def GetBitmapColumn(self): + return 22 + + +class HLICLBRMethod(HLICLBRItem): + def GetBitmapColumn(self): + return 22 + + +class HLIModuleItem(hierlist.HierListItem): + def __init__(self, path): + hierlist.HierListItem.__init__(self) + self.path = path + + def GetText(self): + return os.path.split(self.path)[1] + " (module)" + + def IsExpandable(self): + return 1 + + def TakeDefaultAction(self): + win32ui.GetApp().OpenDocumentFile(self.path) + + def GetBitmapColumn(self): + col = 4 # Default + try: + if win32api.GetFileAttributes(self.path) & win32con.FILE_ATTRIBUTE_READONLY: + col = 5 + except win32api.error: + pass + return col + + def GetSubList(self): + mod, path = pywin.framework.scriptutils.GetPackageModuleName(self.path) + win32ui.SetStatusText("Building class list - please wait...", 1) + win32ui.DoWaitCursor(1) + try: + try: + reader = pyclbr.readmodule_ex # Post 1.5.2 interface. + extra_msg = " or functions" + except AttributeError: + reader = pyclbr.readmodule + extra_msg = "" + data = reader(mod, [path]) + if data: + ret = [] + for item in data.values(): + if ( + item.__class__ != pyclbr.Class + ): # ie, it is a pyclbr Function instance (only introduced post 1.5.2) + ret.append(HLICLBRFunction(item, " (function)")) + else: + ret.append(HLICLBRClass(item, " (class)")) + ret.sort() + return ret + else: + return [HLIErrorItem("No Python classes%s in module." % (extra_msg,))] + finally: + win32ui.DoWaitCursor(0) + win32ui.SetStatusText(win32ui.LoadString(afxres.AFX_IDS_IDLEMESSAGE)) + + +def MakePathSubList(path): + ret = [] + for filename in glob.glob(os.path.join(path, "*")): + if os.path.isdir(filename) and os.path.isfile( + os.path.join(filename, "__init__.py") + ): + ret.append(HLIDirectoryItem(filename, os.path.split(filename)[1])) + else: + if os.path.splitext(filename)[1].lower() in [".py", ".pyw"]: + ret.append(HLIModuleItem(filename)) + return ret + + +class HLIDirectoryItem(hierlist.HierListItem): + def __init__(self, path, displayName=None, bSubDirs=0): + hierlist.HierListItem.__init__(self) + self.path = path + self.bSubDirs = bSubDirs + if displayName: + self.displayName = displayName + else: + self.displayName = path + + def IsExpandable(self): + return 1 + + def GetText(self): + return self.displayName + + def GetSubList(self): + ret = MakePathSubList(self.path) + if ( + os.path.split(self.path)[1] == "win32com" + ): # Complete and utter hack for win32com. + try: + path = win32api.GetFullPathName( + os.path.join(self.path, "..\\win32comext") + ) + ret = ret + MakePathSubList(path) + except win32ui.error: + pass + return ret + + +class HLIProjectRoot(hierlist.HierListItem): + def __init__(self, projectName, displayName=None): + hierlist.HierListItem.__init__(self) + self.projectName = projectName + self.displayName = displayName or projectName + + def GetText(self): + return self.displayName + + def IsExpandable(self): + return 1 + + def GetSubList(self): + paths = regutil.GetRegisteredNamedPath(self.projectName) + pathList = paths.split(";") + if len(pathList) == 1: # Single dir - dont bother putting the dir in + ret = MakePathSubList(pathList[0]) + else: + ret = list(map(HLIDirectoryItem, pathList)) + return ret + + +class HLIRoot(hierlist.HierListItem): + def __init__(self): + hierlist.HierListItem.__init__(self) + + def IsExpandable(self): + return 1 + + def GetSubList(self): + keyStr = regutil.BuildDefaultPythonKey() + "\\PythonPath" + hKey = win32api.RegOpenKey(regutil.GetRootKey(), keyStr) + try: + ret = [] + ret.append(HLIProjectRoot("", "Standard Python Library")) # The core path. + index = 0 + while 1: + try: + ret.append(HLIProjectRoot(win32api.RegEnumKey(hKey, index))) + index = index + 1 + except win32api.error: + break + return ret + finally: + win32api.RegCloseKey(hKey) + + +class dynamic_browser(dialog.Dialog): + style = win32con.WS_OVERLAPPEDWINDOW | win32con.WS_VISIBLE + cs = ( + win32con.WS_CHILD + | win32con.WS_VISIBLE + | commctrl.TVS_HASLINES + | commctrl.TVS_LINESATROOT + | commctrl.TVS_HASBUTTONS + ) + + dt = [ + ["Python Projects", (0, 0, 200, 200), style, None, (8, "MS Sans Serif")], + ["SysTreeView32", None, win32ui.IDC_LIST1, (0, 0, 200, 200), cs], + ] + + def __init__(self, hli_root): + dialog.Dialog.__init__(self, self.dt) + self.hier_list = hierlist.HierListWithItems(hli_root, win32ui.IDB_BROWSER_HIER) + self.HookMessage(self.on_size, win32con.WM_SIZE) + + def OnInitDialog(self): + self.hier_list.HierInit(self) + return dialog.Dialog.OnInitDialog(self) + + def on_size(self, params): + lparam = params[3] + w = win32api.LOWORD(lparam) + h = win32api.HIWORD(lparam) + self.GetDlgItem(win32ui.IDC_LIST1).MoveWindow((0, 0, w, h)) + + +def BrowseDialog(): + root = HLIRoot() + if not root.IsExpandable(): + raise TypeError( + "Browse() argument must have __dict__ attribute, or be a Browser supported type" + ) + + dlg = dynamic_browser(root) + dlg.CreateWindow() + + +def DockableBrowserCreator(parent): + root = HLIRoot() + hl = hierlist.HierListWithItems(root, win32ui.IDB_BROWSER_HIER) + + style = ( + win32con.WS_CHILD + | win32con.WS_VISIBLE + | win32con.WS_BORDER + | commctrl.TVS_HASLINES + | commctrl.TVS_LINESATROOT + | commctrl.TVS_HASBUTTONS + ) + + control = win32ui.CreateTreeCtrl() + control.CreateWindow(style, (0, 0, 150, 300), parent, win32ui.IDC_LIST1) + list = hl.HierInit(parent, control) + return control + + +def DockablePathBrowser(): + import pywin.docking.DockingBar + + bar = pywin.docking.DockingBar.DockingBar() + bar.CreateWindow( + win32ui.GetMainFrame(), DockableBrowserCreator, "Path Browser", 0x8E0A + ) + bar.SetBarStyle( + bar.GetBarStyle() + | afxres.CBRS_TOOLTIPS + | afxres.CBRS_FLYBY + | afxres.CBRS_SIZE_DYNAMIC + ) + bar.EnableDocking(afxres.CBRS_ALIGN_ANY) + win32ui.GetMainFrame().DockControlBar(bar) + + +# The "default" entry point +Browse = DockablePathBrowser diff --git a/myenv/Lib/site-packages/pythonwin/pywin/tools/browser.py b/myenv/Lib/site-packages/pythonwin/pywin/tools/browser.py new file mode 100644 index 000000000..9d9a193db --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/tools/browser.py @@ -0,0 +1,517 @@ +# basic module browser. + +# usage: +# >>> import browser +# >>> browser.Browse() +# or +# >>> browser.Browse(your_module) +import sys +import types + +import __main__ +import win32ui +from pywin.mfc import dialog + +from . import hierlist + +special_names = ["__doc__", "__name__", "__self__"] + + +# +# HierList items +class HLIPythonObject(hierlist.HierListItem): + def __init__(self, myobject=None, name=None): + hierlist.HierListItem.__init__(self) + self.myobject = myobject + self.knownExpandable = None + if name: + self.name = name + else: + try: + self.name = myobject.__name__ + except (AttributeError, TypeError): + try: + r = repr(myobject) + if len(r) > 20: + r = r[:20] + "..." + self.name = r + except (AttributeError, TypeError): + self.name = "???" + + def __lt__(self, other): + return self.name < other.name + + def __eq__(self, other): + return self.name == other.name + + def __repr__(self): + try: + type = self.GetHLIType() + except: + type = "Generic" + return ( + "HLIPythonObject(" + + type + + ") - name: " + + self.name + + " object: " + + repr(self.myobject) + ) + + def GetText(self): + try: + return str(self.name) + " (" + self.GetHLIType() + ")" + except AttributeError: + return str(self.name) + " = " + repr(self.myobject) + + def InsertDocString(self, lst): + ob = None + try: + ob = self.myobject.__doc__ + except (AttributeError, TypeError): + pass + # I don't quite grok descriptors enough to know how to + # best hook them up. Eg: + # >>> object.__getattribute__.__class__.__doc__ + # + if ob and isinstance(ob, str): + lst.insert(0, HLIDocString(ob, "Doc")) + + def GetSubList(self): + ret = [] + try: + for key, ob in self.myobject.__dict__.items(): + if key not in special_names: + ret.append(MakeHLI(ob, key)) + except (AttributeError, TypeError): + pass + try: + for name in self.myobject.__methods__: + ret.append(HLIMethod(name)) # no MakeHLI, as cant auto detect + except (AttributeError, TypeError): + pass + try: + for member in self.myobject.__members__: + if not member in special_names: + ret.append(MakeHLI(getattr(self.myobject, member), member)) + except (AttributeError, TypeError): + pass + ret.sort() + self.InsertDocString(ret) + return ret + + # if the has a dict, it is expandable. + def IsExpandable(self): + if self.knownExpandable is None: + self.knownExpandable = self.CalculateIsExpandable() + return self.knownExpandable + + def CalculateIsExpandable(self): + if hasattr(self.myobject, "__doc__"): + return 1 + try: + for key in self.myobject.__dict__.keys(): + if key not in special_names: + return 1 + except (AttributeError, TypeError): + pass + try: + self.myobject.__methods__ + return 1 + except (AttributeError, TypeError): + pass + try: + for item in self.myobject.__members__: + if item not in special_names: + return 1 + except (AttributeError, TypeError): + pass + return 0 + + def GetBitmapColumn(self): + if self.IsExpandable(): + return 0 + else: + return 4 + + def TakeDefaultAction(self): + ShowObject(self.myobject, self.name) + + +class HLIDocString(HLIPythonObject): + def GetHLIType(self): + return "DocString" + + def GetText(self): + return self.myobject.strip() + + def IsExpandable(self): + return 0 + + def GetBitmapColumn(self): + return 6 + + +class HLIModule(HLIPythonObject): + def GetHLIType(self): + return "Module" + + +class HLIFrame(HLIPythonObject): + def GetHLIType(self): + return "Stack Frame" + + +class HLITraceback(HLIPythonObject): + def GetHLIType(self): + return "Traceback" + + +class HLIClass(HLIPythonObject): + def GetHLIType(self): + return "Class" + + def GetSubList(self): + ret = [] + for base in self.myobject.__bases__: + ret.append(MakeHLI(base, "Base class: " + base.__name__)) + ret = ret + HLIPythonObject.GetSubList(self) + return ret + + +class HLIMethod(HLIPythonObject): + # myobject is just a string for methods. + def GetHLIType(self): + return "Method" + + def GetText(self): + return "Method: " + self.myobject + "()" + + +class HLICode(HLIPythonObject): + def GetHLIType(self): + return "Code" + + def IsExpandable(self): + return self.myobject + + def GetSubList(self): + ret = [] + ret.append(MakeHLI(self.myobject.co_consts, "Constants (co_consts)")) + ret.append(MakeHLI(self.myobject.co_names, "Names (co_names)")) + ret.append(MakeHLI(self.myobject.co_filename, "Filename (co_filename)")) + ret.append(MakeHLI(self.myobject.co_argcount, "Number of args (co_argcount)")) + ret.append(MakeHLI(self.myobject.co_varnames, "Param names (co_varnames)")) + + return ret + + +class HLIInstance(HLIPythonObject): + def GetHLIType(self): + return "Instance" + + def GetText(self): + return ( + str(self.name) + + " (Instance of class " + + str(self.myobject.__class__.__name__) + + ")" + ) + + def IsExpandable(self): + return 1 + + def GetSubList(self): + ret = [] + ret.append(MakeHLI(self.myobject.__class__)) + ret = ret + HLIPythonObject.GetSubList(self) + return ret + + +class HLIBuiltinFunction(HLIPythonObject): + def GetHLIType(self): + return "Builtin Function" + + +class HLIFunction(HLIPythonObject): + def GetHLIType(self): + return "Function" + + def IsExpandable(self): + return 1 + + def GetSubList(self): + ret = [] + # ret.append( MakeHLI( self.myobject.func_argcount, "Arg Count" )) + try: + ret.append(MakeHLI(self.myobject.func_argdefs, "Arg Defs")) + except AttributeError: + pass + try: + code = self.myobject.__code__ + globs = self.myobject.__globals__ + except AttributeError: + # must be py2.5 or earlier... + code = self.myobject.func_code + globs = self.myobject.func_globals + ret.append(MakeHLI(code, "Code")) + ret.append(MakeHLI(globs, "Globals")) + self.InsertDocString(ret) + return ret + + +class HLISeq(HLIPythonObject): + def GetHLIType(self): + return "Sequence (abstract!)" + + def IsExpandable(self): + return len(self.myobject) > 0 + + def GetSubList(self): + ret = [] + pos = 0 + for item in self.myobject: + ret.append(MakeHLI(item, "[" + str(pos) + "]")) + pos = pos + 1 + self.InsertDocString(ret) + return ret + + +class HLIList(HLISeq): + def GetHLIType(self): + return "List" + + +class HLITuple(HLISeq): + def GetHLIType(self): + return "Tuple" + + +class HLIDict(HLIPythonObject): + def GetHLIType(self): + return "Dict" + + def IsExpandable(self): + try: + self.myobject.__doc__ + return 1 + except (AttributeError, TypeError): + return len(self.myobject) > 0 + + def GetSubList(self): + ret = [] + keys = list(self.myobject.keys()) + keys.sort() + for key in keys: + ob = self.myobject[key] + ret.append(MakeHLI(ob, str(key))) + self.InsertDocString(ret) + return ret + + +# In Python 1.6, strings and Unicode have builtin methods, but we dont really want to see these +class HLIString(HLIPythonObject): + def IsExpandable(self): + return 0 + + +TypeMap = { + type: HLIClass, + types.FunctionType: HLIFunction, + tuple: HLITuple, + dict: HLIDict, + list: HLIList, + types.ModuleType: HLIModule, + types.CodeType: HLICode, + types.BuiltinFunctionType: HLIBuiltinFunction, + types.FrameType: HLIFrame, + types.TracebackType: HLITraceback, + str: HLIString, + int: HLIPythonObject, + bool: HLIPythonObject, + float: HLIPythonObject, +} + + +def MakeHLI(ob, name=None): + try: + cls = TypeMap[type(ob)] + except KeyError: + # hrmph - this check gets more and more bogus as Python + # improves. Its possible we should just *always* use + # HLIInstance? + if hasattr(ob, "__class__"): # 'new style' class + cls = HLIInstance + else: + cls = HLIPythonObject + return cls(ob, name) + + +######################################### +# +# Dialog related. + + +class DialogShowObject(dialog.Dialog): + def __init__(self, object, title): + self.object = object + self.title = title + dialog.Dialog.__init__(self, win32ui.IDD_LARGE_EDIT) + + def OnInitDialog(self): + import re + + self.SetWindowText(self.title) + self.edit = self.GetDlgItem(win32ui.IDC_EDIT1) + try: + strval = str(self.object) + except: + t, v, tb = sys.exc_info() + strval = "Exception getting object value\n\n%s:%s" % (t, v) + tb = None + strval = re.sub("\n", "\r\n", strval) + self.edit.ReplaceSel(strval) + + +def ShowObject(object, title): + dlg = DialogShowObject(object, title) + dlg.DoModal() + + +# And some mods for a sizable dialog from Sam Rushing! +import commctrl +import win32api +import win32con + + +class dynamic_browser(dialog.Dialog): + style = win32con.WS_OVERLAPPEDWINDOW | win32con.WS_VISIBLE + cs = ( + win32con.WS_CHILD + | win32con.WS_VISIBLE + | commctrl.TVS_HASLINES + | commctrl.TVS_LINESATROOT + | commctrl.TVS_HASBUTTONS + ) + + dt = [ + ["Python Object Browser", (0, 0, 200, 200), style, None, (8, "MS Sans Serif")], + ["SysTreeView32", None, win32ui.IDC_LIST1, (0, 0, 200, 200), cs], + ] + + def __init__(self, hli_root): + dialog.Dialog.__init__(self, self.dt) + self.hier_list = hierlist.HierListWithItems(hli_root, win32ui.IDB_BROWSER_HIER) + self.HookMessage(self.on_size, win32con.WM_SIZE) + + def OnInitDialog(self): + self.hier_list.HierInit(self) + return dialog.Dialog.OnInitDialog(self) + + def OnOK(self): + self.hier_list.HierTerm() + self.hier_list = None + return self._obj_.OnOK() + + def OnCancel(self): + self.hier_list.HierTerm() + self.hier_list = None + return self._obj_.OnCancel() + + def on_size(self, params): + lparam = params[3] + w = win32api.LOWORD(lparam) + h = win32api.HIWORD(lparam) + self.GetDlgItem(win32ui.IDC_LIST1).MoveWindow((0, 0, w, h)) + + +def Browse(ob=__main__): + "Browse the argument, or the main dictionary" + root = MakeHLI(ob, "root") + if not root.IsExpandable(): + raise TypeError( + "Browse() argument must have __dict__ attribute, or be a Browser supported type" + ) + + dlg = dynamic_browser(root) + dlg.CreateWindow() + return dlg + + +# +# +# Classes for using the browser in an MDI window, rather than a dialog +# +from pywin.mfc import docview + + +class BrowserTemplate(docview.DocTemplate): + def __init__(self): + docview.DocTemplate.__init__( + self, win32ui.IDR_PYTHONTYPE, BrowserDocument, None, BrowserView + ) + + def OpenObject(self, root): # Use this instead of OpenDocumentFile. + # Look for existing open document + for doc in self.GetDocumentList(): + if doc.root == root: + doc.GetFirstView().ActivateFrame() + return doc + # not found - new one. + doc = BrowserDocument(self, root) + frame = self.CreateNewFrame(doc) + doc.OnNewDocument() + self.InitialUpdateFrame(frame, doc, 1) + return doc + + +class BrowserDocument(docview.Document): + def __init__(self, template, root): + docview.Document.__init__(self, template) + self.root = root + self.SetTitle("Browser: " + root.name) + + def OnOpenDocument(self, name): + raise TypeError("This template can not open files") + return 0 + + +class BrowserView(docview.TreeView): + def OnInitialUpdate(self): + import commctrl + + rc = self._obj_.OnInitialUpdate() + list = hierlist.HierListWithItems( + self.GetDocument().root, + win32ui.IDB_BROWSER_HIER, + win32ui.AFX_IDW_PANE_FIRST, + ) + list.HierInit(self.GetParent()) + list.SetStyle( + commctrl.TVS_HASLINES | commctrl.TVS_LINESATROOT | commctrl.TVS_HASBUTTONS + ) + return rc + + +template = None + + +def MakeTemplate(): + global template + if template is None: + template = ( + BrowserTemplate() + ) # win32ui.IDR_PYTHONTYPE, BrowserDocument, None, BrowserView) + + +def BrowseMDI(ob=__main__): + """Browse an object using an MDI window.""" + + MakeTemplate() + root = MakeHLI(ob, repr(ob)) + if not root.IsExpandable(): + raise TypeError( + "Browse() argument must have __dict__ attribute, or be a Browser supported type" + ) + + template.OpenObject(root) diff --git a/myenv/Lib/site-packages/pythonwin/pywin/tools/hierlist.py b/myenv/Lib/site-packages/pythonwin/pywin/tools/hierlist.py new file mode 100644 index 000000000..a60b73247 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/tools/hierlist.py @@ -0,0 +1,367 @@ +# hierlist +# +# IMPORTANT - Please read before using. + +# This module exposes an API for a Hierarchical Tree Control. +# Previously, a custom tree control was included in Pythonwin which +# has an API very similar to this. + +# The current control used is the common "Tree Control". This module exists now +# to provide an API similar to the old control, but for the new Tree control. + +# If you need to use the Tree Control, you may still find this API a reasonable +# choice. However, you should investigate using the tree control directly +# to provide maximum flexibility (but with extra work). + +import sys + +import commctrl +import win32api +import win32con +import win32ui +from pywin.mfc import dialog, docview, object, window +from win32api import RGB + + +# helper to get the text of an arbitary item +def GetItemText(item): + if type(item) == type(()) or type(item) == type([]): + use = item[0] + else: + use = item + if type(use) == type(""): + return use + else: + return repr(item) + + +class HierDialog(dialog.Dialog): + def __init__( + self, + title, + hierList, + bitmapID=win32ui.IDB_HIERFOLDERS, + dlgID=win32ui.IDD_TREE, + dll=None, + childListBoxID=win32ui.IDC_LIST1, + ): + dialog.Dialog.__init__(self, dlgID, dll) # reuse this dialog. + self.hierList = hierList + self.dlgID = dlgID + self.title = title + + # self.childListBoxID = childListBoxID + def OnInitDialog(self): + self.SetWindowText(self.title) + self.hierList.HierInit(self) + return dialog.Dialog.OnInitDialog(self) + + +class HierList(object.Object): + def __init__( + self, root, bitmapID=win32ui.IDB_HIERFOLDERS, listBoxId=None, bitmapMask=None + ): # used to create object. + self.listControl = None + self.bitmapID = bitmapID + self.root = root + self.listBoxId = listBoxId + self.itemHandleMap = {} + self.filledItemHandlesMap = {} + self.bitmapMask = bitmapMask + + def __getattr__(self, attr): + try: + return getattr(self.listControl, attr) + except AttributeError: + return object.Object.__getattr__(self, attr) + + def ItemFromHandle(self, handle): + return self.itemHandleMap[handle] + + def SetStyle(self, newStyle): + hwnd = self.listControl.GetSafeHwnd() + style = win32api.GetWindowLong(hwnd, win32con.GWL_STYLE) + win32api.SetWindowLong(hwnd, win32con.GWL_STYLE, (style | newStyle)) + + def HierInit(self, parent, listControl=None): # Used when window first exists. + # this also calls "Create" on the listbox. + # params - id of listbbox, ID of bitmap, size of bitmaps + if self.bitmapMask is None: + bitmapMask = RGB(0, 0, 255) + else: + bitmapMask = self.bitmapMask + self.imageList = win32ui.CreateImageList(self.bitmapID, 16, 0, bitmapMask) + if listControl is None: + if self.listBoxId is None: + self.listBoxId = win32ui.IDC_LIST1 + self.listControl = parent.GetDlgItem(self.listBoxId) + else: + self.listControl = listControl + lbid = listControl.GetDlgCtrlID() + assert self.listBoxId is None or self.listBoxId == lbid, ( + "An invalid listbox control ID has been specified (specified as %s, but exists as %s)" + % (self.listBoxId, lbid) + ) + self.listBoxId = lbid + self.listControl.SetImageList(self.imageList, commctrl.LVSIL_NORMAL) + # self.list.AttachObject(self) + + ## ??? Need a better way to do this - either some way to detect if it's compiled with UNICODE + ## defined, and/or a way to switch the constants based on UNICODE ??? + if sys.version_info[0] < 3: + parent.HookNotify(self.OnTreeItemExpanding, commctrl.TVN_ITEMEXPANDINGA) + parent.HookNotify(self.OnTreeItemSelChanged, commctrl.TVN_SELCHANGEDA) + else: + parent.HookNotify(self.OnTreeItemExpanding, commctrl.TVN_ITEMEXPANDINGW) + parent.HookNotify(self.OnTreeItemSelChanged, commctrl.TVN_SELCHANGEDW) + parent.HookNotify(self.OnTreeItemDoubleClick, commctrl.NM_DBLCLK) + self.notify_parent = parent + + if self.root: + self.AcceptRoot(self.root) + + def DeleteAllItems(self): + self.listControl.DeleteAllItems() + self.root = None + self.itemHandleMap = {} + self.filledItemHandlesMap = {} + + def HierTerm(self): + # Dont want notifies as we kill the list. + parent = self.notify_parent # GetParentFrame() + if sys.version_info[0] < 3: + parent.HookNotify(None, commctrl.TVN_ITEMEXPANDINGA) + parent.HookNotify(None, commctrl.TVN_SELCHANGEDA) + else: + parent.HookNotify(None, commctrl.TVN_ITEMEXPANDINGW) + parent.HookNotify(None, commctrl.TVN_SELCHANGEDW) + parent.HookNotify(None, commctrl.NM_DBLCLK) + + self.DeleteAllItems() + self.listControl = None + self.notify_parent = None # Break a possible cycle + + def OnTreeItemDoubleClick(self, info, extra): + (hwndFrom, idFrom, code) = info + if idFrom != self.listBoxId: + return None + item = self.itemHandleMap[self.listControl.GetSelectedItem()] + self.TakeDefaultAction(item) + return 1 + + def OnTreeItemExpanding(self, info, extra): + (hwndFrom, idFrom, code) = info + if idFrom != self.listBoxId: + return None + action, itemOld, itemNew, pt = extra + itemHandle = itemNew[0] + if itemHandle not in self.filledItemHandlesMap: + item = self.itemHandleMap[itemHandle] + self.AddSubList(itemHandle, self.GetSubList(item)) + self.filledItemHandlesMap[itemHandle] = None + return 0 + + def OnTreeItemSelChanged(self, info, extra): + (hwndFrom, idFrom, code) = info + if idFrom != self.listBoxId: + return None + action, itemOld, itemNew, pt = extra + itemHandle = itemNew[0] + item = self.itemHandleMap[itemHandle] + self.PerformItemSelected(item) + return 1 + + def AddSubList(self, parentHandle, subItems): + for item in subItems: + self.AddItem(parentHandle, item) + + def AddItem(self, parentHandle, item, hInsertAfter=commctrl.TVI_LAST): + text = self.GetText(item) + if self.IsExpandable(item): + cItems = 1 # Trick it !! + else: + cItems = 0 + bitmapCol = self.GetBitmapColumn(item) + bitmapSel = self.GetSelectedBitmapColumn(item) + if bitmapSel is None: + bitmapSel = bitmapCol + ## if type(text) is str: + ## text = text.encode("mbcs") + hitem = self.listControl.InsertItem( + parentHandle, + hInsertAfter, + (None, None, None, text, bitmapCol, bitmapSel, cItems, 0), + ) + self.itemHandleMap[hitem] = item + return hitem + + def _GetChildHandles(self, handle): + ret = [] + try: + handle = self.listControl.GetChildItem(handle) + while 1: + ret.append(handle) + handle = self.listControl.GetNextItem(handle, commctrl.TVGN_NEXT) + except win32ui.error: + # out of children + pass + return ret + + def Refresh(self, hparent=None): + # Attempt to refresh the given item's sub-entries, but maintain the tree state + # (ie, the selected item, expanded items, etc) + if hparent is None: + hparent = commctrl.TVI_ROOT + if hparent not in self.filledItemHandlesMap: + # This item has never been expanded, so no refresh can possibly be required. + return + root_item = self.itemHandleMap[hparent] + old_handles = self._GetChildHandles(hparent) + old_items = list(map(self.ItemFromHandle, old_handles)) + new_items = self.GetSubList(root_item) + # Now an inefficient technique for synching the items. + inew = 0 + hAfter = commctrl.TVI_FIRST + for iold in range(len(old_items)): + inewlook = inew + matched = 0 + while inewlook < len(new_items): + if old_items[iold] == new_items[inewlook]: + matched = 1 + break + inewlook = inewlook + 1 + if matched: + # Insert the new items. + # print "Inserting after", old_items[iold], old_handles[iold] + for i in range(inew, inewlook): + # print "Inserting index %d (%s)" % (i, new_items[i]) + hAfter = self.AddItem(hparent, new_items[i], hAfter) + + inew = inewlook + 1 + # And recursively refresh iold + hold = old_handles[iold] + if hold in self.filledItemHandlesMap: + self.Refresh(hold) + else: + # Remove the deleted items. + # print "Deleting %d (%s)" % (iold, old_items[iold]) + hdelete = old_handles[iold] + # First recurse and remove the children from the map. + for hchild in self._GetChildHandles(hdelete): + del self.itemHandleMap[hchild] + if hchild in self.filledItemHandlesMap: + del self.filledItemHandlesMap[hchild] + self.listControl.DeleteItem(hdelete) + hAfter = old_handles[iold] + # Fill any remaining new items: + for newItem in new_items[inew:]: + # print "Inserting new item", newItem + self.AddItem(hparent, newItem) + + def AcceptRoot(self, root): + self.listControl.DeleteAllItems() + self.itemHandleMap = {commctrl.TVI_ROOT: root} + self.filledItemHandlesMap = {commctrl.TVI_ROOT: root} + subItems = self.GetSubList(root) + self.AddSubList(0, subItems) + + def GetBitmapColumn(self, item): + if self.IsExpandable(item): + return 0 + else: + return 4 + + def GetSelectedBitmapColumn(self, item): + return 0 + + def CheckChangedChildren(self): + return self.listControl.CheckChangedChildren() + + def GetText(self, item): + return GetItemText(item) + + def PerformItemSelected(self, item): + try: + win32ui.SetStatusText("Selected " + self.GetText(item)) + except win32ui.error: # No status bar! + pass + + def TakeDefaultAction(self, item): + win32ui.MessageBox("Got item " + self.GetText(item)) + + +########################################################################## +# +# Classes for use with seperate HierListItems. +# +# +class HierListWithItems(HierList): + def __init__( + self, root, bitmapID=win32ui.IDB_HIERFOLDERS, listBoxID=None, bitmapMask=None + ): # used to create object. + HierList.__init__(self, root, bitmapID, listBoxID, bitmapMask) + + def DelegateCall(self, fn): + return fn() + + def GetBitmapColumn(self, item): + rc = self.DelegateCall(item.GetBitmapColumn) + if rc is None: + rc = HierList.GetBitmapColumn(self, item) + return rc + + def GetSelectedBitmapColumn(self, item): + return self.DelegateCall(item.GetSelectedBitmapColumn) + + def IsExpandable(self, item): + return self.DelegateCall(item.IsExpandable) + + def GetText(self, item): + return self.DelegateCall(item.GetText) + + def GetSubList(self, item): + return self.DelegateCall(item.GetSubList) + + def PerformItemSelected(self, item): + func = getattr(item, "PerformItemSelected", None) + if func is None: + return HierList.PerformItemSelected(self, item) + else: + return self.DelegateCall(func) + + def TakeDefaultAction(self, item): + func = getattr(item, "TakeDefaultAction", None) + if func is None: + return HierList.TakeDefaultAction(self, item) + else: + return self.DelegateCall(func) + + +# A hier list item - for use with a HierListWithItems +class HierListItem: + def __init__(self): + pass + + def GetText(self): + pass + + def GetSubList(self): + pass + + def IsExpandable(self): + pass + + def GetBitmapColumn(self): + return None # indicate he should do it. + + def GetSelectedBitmapColumn(self): + return None # same as other + + # for py3k/rich-comp sorting compatibility. + def __lt__(self, other): + # we want unrelated items to be sortable... + return id(self) < id(other) + + # for py3k/rich-comp equality compatibility. + def __eq__(self, other): + return False diff --git a/myenv/Lib/site-packages/pythonwin/pywin/tools/regedit.py b/myenv/Lib/site-packages/pythonwin/pywin/tools/regedit.py new file mode 100644 index 000000000..b94d3050f --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/tools/regedit.py @@ -0,0 +1,386 @@ +# Regedit - a Registry Editor for Python + +import commctrl +import regutil +import win32api +import win32con +import win32ui +from pywin.mfc import dialog, docview, window + +from . import hierlist + + +def SafeApply(fn, args, err_desc=""): + try: + fn(*args) + return 1 + except win32api.error as exc: + msg = "Error " + err_desc + "\r\n\r\n" + exc.strerror + win32ui.MessageBox(msg) + return 0 + + +class SplitterFrame(window.MDIChildWnd): + def __init__(self): + # call base CreateFrame + self.images = None + window.MDIChildWnd.__init__(self) + + def OnCreateClient(self, cp, context): + splitter = win32ui.CreateSplitter() + doc = context.doc + frame_rect = self.GetWindowRect() + size = ((frame_rect[2] - frame_rect[0]), (frame_rect[3] - frame_rect[1]) // 2) + sub_size = (size[0] // 3, size[1]) + splitter.CreateStatic(self, 1, 2) + # CTreeControl view + self.keysview = RegistryTreeView(doc) + # CListControl view + self.valuesview = RegistryValueView(doc) + + splitter.CreatePane(self.keysview, 0, 0, (sub_size)) + splitter.CreatePane(self.valuesview, 0, 1, (0, 0)) # size ignored. + splitter.SetRowInfo(0, size[1], 0) + # Setup items in the imagelist + + return 1 + + def OnItemDoubleClick(self, info, extra): + (hwndFrom, idFrom, code) = info + if idFrom == win32ui.AFX_IDW_PANE_FIRST: + # Tree control + return None + elif idFrom == win32ui.AFX_IDW_PANE_FIRST + 1: + item = self.keysview.SelectedItem() + self.valuesview.EditValue(item) + return 0 + # List control + else: + return None # Pass it on + + def PerformItemSelected(self, item): + return self.valuesview.UpdateForRegItem(item) + + def OnDestroy(self, msg): + window.MDIChildWnd.OnDestroy(self, msg) + if self.images: + self.images.DeleteImageList() + self.images = None + + +class RegistryTreeView(docview.TreeView): + def OnInitialUpdate(self): + rc = self._obj_.OnInitialUpdate() + self.frame = self.GetParent().GetParent() + self.hierList = hierlist.HierListWithItems( + self.GetHLIRoot(), win32ui.IDB_HIERFOLDERS, win32ui.AFX_IDW_PANE_FIRST + ) + self.hierList.HierInit(self.frame, self.GetTreeCtrl()) + self.hierList.SetStyle( + commctrl.TVS_HASLINES | commctrl.TVS_LINESATROOT | commctrl.TVS_HASBUTTONS + ) + self.hierList.PerformItemSelected = self.PerformItemSelected + + self.frame.HookNotify(self.frame.OnItemDoubleClick, commctrl.NM_DBLCLK) + self.frame.HookNotify(self.OnItemRightClick, commctrl.NM_RCLICK) + + # self.HookMessage(self.OnItemRightClick, win32con.WM_RBUTTONUP) + + def GetHLIRoot(self): + doc = self.GetDocument() + regroot = doc.root + subkey = doc.subkey + return HLIRegistryKey(regroot, subkey, "Root") + + def OnItemRightClick(self, notify_data, extra): + # First select the item we right-clicked on. + pt = self.ScreenToClient(win32api.GetCursorPos()) + flags, hItem = self.HitTest(pt) + if hItem == 0 or commctrl.TVHT_ONITEM & flags == 0: + return None + self.Select(hItem, commctrl.TVGN_CARET) + + menu = win32ui.CreatePopupMenu() + menu.AppendMenu(win32con.MF_STRING | win32con.MF_ENABLED, 1000, "Add Key") + menu.AppendMenu(win32con.MF_STRING | win32con.MF_ENABLED, 1001, "Add Value") + menu.AppendMenu(win32con.MF_STRING | win32con.MF_ENABLED, 1002, "Delete Key") + self.HookCommand(self.OnAddKey, 1000) + self.HookCommand(self.OnAddValue, 1001) + self.HookCommand(self.OnDeleteKey, 1002) + menu.TrackPopupMenu(win32api.GetCursorPos()) # track at mouse position. + return None + + def OnDeleteKey(self, command, code): + hitem = self.hierList.GetSelectedItem() + item = self.hierList.ItemFromHandle(hitem) + msg = "Are you sure you wish to delete the key '%s'?" % (item.keyName,) + id = win32ui.MessageBox(msg, None, win32con.MB_YESNO) + if id != win32con.IDYES: + return + if SafeApply( + win32api.RegDeleteKey, (item.keyRoot, item.keyName), "deleting registry key" + ): + # Get the items parent. + try: + hparent = self.GetParentItem(hitem) + except win32ui.error: + hparent = None + self.hierList.Refresh(hparent) + + def OnAddKey(self, command, code): + from pywin.mfc import dialog + + val = dialog.GetSimpleInput("New key name", "", "Add new key") + if val is None: + return # cancelled. + hitem = self.hierList.GetSelectedItem() + item = self.hierList.ItemFromHandle(hitem) + if SafeApply(win32api.RegCreateKey, (item.keyRoot, item.keyName + "\\" + val)): + self.hierList.Refresh(hitem) + + def OnAddValue(self, command, code): + from pywin.mfc import dialog + + val = dialog.GetSimpleInput("New value", "", "Add new value") + if val is None: + return # cancelled. + hitem = self.hierList.GetSelectedItem() + item = self.hierList.ItemFromHandle(hitem) + if SafeApply( + win32api.RegSetValue, (item.keyRoot, item.keyName, win32con.REG_SZ, val) + ): + # Simply re-select the current item to refresh the right spitter. + self.PerformItemSelected(item) + + # self.Select(hitem, commctrl.TVGN_CARET) + + def PerformItemSelected(self, item): + return self.frame.PerformItemSelected(item) + + def SelectedItem(self): + return self.hierList.ItemFromHandle(self.hierList.GetSelectedItem()) + + def SearchSelectedItem(self): + handle = self.hierList.GetChildItem(0) + while 1: + # print "State is", self.hierList.GetItemState(handle, -1) + if self.hierList.GetItemState(handle, commctrl.TVIS_SELECTED): + # print "Item is ", self.hierList.ItemFromHandle(handle) + return self.hierList.ItemFromHandle(handle) + handle = self.hierList.GetNextSiblingItem(handle) + + +class RegistryValueView(docview.ListView): + def OnInitialUpdate(self): + hwnd = self._obj_.GetSafeHwnd() + style = win32api.GetWindowLong(hwnd, win32con.GWL_STYLE) + win32api.SetWindowLong( + hwnd, + win32con.GWL_STYLE, + (style & ~commctrl.LVS_TYPEMASK) | commctrl.LVS_REPORT, + ) + + itemDetails = (commctrl.LVCFMT_LEFT, 100, "Name", 0) + self.InsertColumn(0, itemDetails) + itemDetails = (commctrl.LVCFMT_LEFT, 500, "Data", 0) + self.InsertColumn(1, itemDetails) + + def UpdateForRegItem(self, item): + self.DeleteAllItems() + hkey = win32api.RegOpenKey(item.keyRoot, item.keyName) + try: + valNum = 0 + ret = [] + while 1: + try: + res = win32api.RegEnumValue(hkey, valNum) + except win32api.error: + break + name = res[0] + if not name: + name = "(Default)" + self.InsertItem(valNum, name) + self.SetItemText(valNum, 1, str(res[1])) + valNum = valNum + 1 + finally: + win32api.RegCloseKey(hkey) + + def EditValue(self, item): + # Edit the current value + class EditDialog(dialog.Dialog): + def __init__(self, item): + self.item = item + dialog.Dialog.__init__(self, win32ui.IDD_LARGE_EDIT) + + def OnInitDialog(self): + self.SetWindowText("Enter new value") + self.GetDlgItem(win32con.IDCANCEL).ShowWindow(win32con.SW_SHOW) + self.edit = self.GetDlgItem(win32ui.IDC_EDIT1) + # Modify the edit windows style + style = win32api.GetWindowLong( + self.edit.GetSafeHwnd(), win32con.GWL_STYLE + ) + style = style & (~win32con.ES_WANTRETURN) + win32api.SetWindowLong( + self.edit.GetSafeHwnd(), win32con.GWL_STYLE, style + ) + self.edit.SetWindowText(str(self.item)) + self.edit.SetSel(-1) + return dialog.Dialog.OnInitDialog(self) + + def OnDestroy(self, msg): + self.newvalue = self.edit.GetWindowText() + + try: + index = self.GetNextItem(-1, commctrl.LVNI_SELECTED) + except win32ui.error: + return # No item selected. + + if index == 0: + keyVal = "" + else: + keyVal = self.GetItemText(index, 0) + # Query for a new value. + try: + newVal = self.GetItemsCurrentValue(item, keyVal) + except TypeError as details: + win32ui.MessageBox(details) + return + + d = EditDialog(newVal) + if d.DoModal() == win32con.IDOK: + try: + self.SetItemsCurrentValue(item, keyVal, d.newvalue) + except win32api.error as exc: + win32ui.MessageBox("Error setting value\r\n\n%s" % exc.strerror) + self.UpdateForRegItem(item) + + def GetItemsCurrentValue(self, item, valueName): + hkey = win32api.RegOpenKey(item.keyRoot, item.keyName) + try: + val, type = win32api.RegQueryValueEx(hkey, valueName) + if type != win32con.REG_SZ: + raise TypeError("Only strings can be edited") + return val + finally: + win32api.RegCloseKey(hkey) + + def SetItemsCurrentValue(self, item, valueName, value): + # ** Assumes already checked is a string. + hkey = win32api.RegOpenKey( + item.keyRoot, item.keyName, 0, win32con.KEY_SET_VALUE + ) + try: + win32api.RegSetValueEx(hkey, valueName, 0, win32con.REG_SZ, value) + finally: + win32api.RegCloseKey(hkey) + + +class RegTemplate(docview.DocTemplate): + def __init__(self): + docview.DocTemplate.__init__( + self, win32ui.IDR_PYTHONTYPE, None, SplitterFrame, None + ) + + # def InitialUpdateFrame(self, frame, doc, makeVisible=1): + # self._obj_.InitialUpdateFrame(frame, doc, makeVisible) # call default handler. + # frame.InitialUpdateFrame(doc, makeVisible) + + def OpenRegistryKey( + self, root=None, subkey=None + ): # Use this instead of OpenDocumentFile. + # Look for existing open document + if root is None: + root = regutil.GetRootKey() + if subkey is None: + subkey = regutil.BuildDefaultPythonKey() + for doc in self.GetDocumentList(): + if doc.root == root and doc.subkey == subkey: + doc.GetFirstView().ActivateFrame() + return doc + # not found - new one. + doc = RegDocument(self, root, subkey) + frame = self.CreateNewFrame(doc) + doc.OnNewDocument() + self.InitialUpdateFrame(frame, doc, 1) + return doc + + +class RegDocument(docview.Document): + def __init__(self, template, root, subkey): + docview.Document.__init__(self, template) + self.root = root + self.subkey = subkey + self.SetTitle("Registry Editor: " + subkey) + + def OnOpenDocument(self, name): + raise TypeError("This template can not open files") + return 0 + + +class HLIRegistryKey(hierlist.HierListItem): + def __init__(self, keyRoot, keyName, userName): + self.keyRoot = keyRoot + self.keyName = keyName + self.userName = userName + hierlist.HierListItem.__init__(self) + + def __lt__(self, other): + return self.name < other.name + + def __eq__(self, other): + return ( + self.keyRoot == other.keyRoot + and self.keyName == other.keyName + and self.userName == other.userName + ) + + def __repr__(self): + return "<%s with root=%s, key=%s>" % ( + self.__class__.__name__, + self.keyRoot, + self.keyName, + ) + + def GetText(self): + return self.userName + + def IsExpandable(self): + # All keys are expandable, even if they currently have zero children. + return 1 + + ## hkey = win32api.RegOpenKey(self.keyRoot, self.keyName) + ## try: + ## keys, vals, dt = win32api.RegQueryInfoKey(hkey) + ## return (keys>0) + ## finally: + ## win32api.RegCloseKey(hkey) + + def GetSubList(self): + hkey = win32api.RegOpenKey(self.keyRoot, self.keyName) + win32ui.DoWaitCursor(1) + try: + keyNum = 0 + ret = [] + while 1: + try: + key = win32api.RegEnumKey(hkey, keyNum) + except win32api.error: + break + ret.append(HLIRegistryKey(self.keyRoot, self.keyName + "\\" + key, key)) + keyNum = keyNum + 1 + finally: + win32api.RegCloseKey(hkey) + win32ui.DoWaitCursor(0) + return ret + + +template = RegTemplate() + + +def EditRegistry(root=None, key=None): + doc = template.OpenRegistryKey(root, key) + + +if __name__ == "__main__": + EditRegistry() diff --git a/myenv/Lib/site-packages/pythonwin/pywin/tools/regpy.py b/myenv/Lib/site-packages/pythonwin/pywin/tools/regpy.py new file mode 100644 index 000000000..11ad63af5 --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/pywin/tools/regpy.py @@ -0,0 +1,81 @@ +# (sort-of) Registry editor +import commctrl +import dialog +import win32con +import win32ui + + +class RegistryControl: + def __init__(self, key): + self.key = key + + +class RegEditPropertyPage(dialog.PropertyPage): + IDC_LISTVIEW = 1000 + + def GetTemplate(self): + "Return the template used to create this dialog" + + w = 152 # Dialog width + h = 122 # Dialog height + SS_STD = win32con.WS_CHILD | win32con.WS_VISIBLE + FRAMEDLG_STD = win32con.WS_CAPTION | win32con.WS_SYSMENU + style = ( + FRAMEDLG_STD + | win32con.WS_VISIBLE + | win32con.DS_SETFONT + | win32con.WS_MINIMIZEBOX + ) + template = [ + [self.caption, (0, 0, w, h), style, None, (8, "Helv")], + ] + lvStyle = ( + SS_STD + | commctrl.LVS_EDITLABELS + | commctrl.LVS_REPORT + | commctrl.LVS_AUTOARRANGE + | commctrl.LVS_ALIGNLEFT + | win32con.WS_BORDER + | win32con.WS_TABSTOP + ) + template.append( + ["SysListView32", "", self.IDC_LISTVIEW, (10, 10, 185, 100), lvStyle] + ) + return template + + +class RegistryPage(RegEditPropertyPage): + def __init__(self): + self.caption = "Path" + RegEditPropertyPage.__init__(self, self.GetTemplate()) + + def OnInitDialog(self): + self.listview = self.GetDlgItem(self.IDC_LISTVIEW) + RegEditPropertyPage.OnInitDialog(self) + # Setup the listview columns + itemDetails = (commctrl.LVCFMT_LEFT, 100, "App", 0) + self.listview.InsertColumn(0, itemDetails) + itemDetails = (commctrl.LVCFMT_LEFT, 1024, "Paths", 0) + self.listview.InsertColumn(1, itemDetails) + + index = self.listview.InsertItem(0, "App") + self.listview.SetItemText(index, 1, "Path") + + +class RegistrySheet(dialog.PropertySheet): + def __init__(self, title): + dialog.PropertySheet.__init__(self, title) + self.HookMessage(self.OnActivate, win32con.WM_ACTIVATE) + + def OnActivate(self, msg): + print("OnAcivate") + + +def t(): + ps = RegistrySheet("Registry Settings") + ps.AddPage(RegistryPage()) + ps.DoModal() + + +if __name__ == "__main__": + t() diff --git a/myenv/Lib/site-packages/pythonwin/scintilla.dll b/myenv/Lib/site-packages/pythonwin/scintilla.dll new file mode 100644 index 000000000..377b9ccd0 Binary files /dev/null and b/myenv/Lib/site-packages/pythonwin/scintilla.dll differ diff --git a/myenv/Lib/site-packages/pythonwin/start_pythonwin.pyw b/myenv/Lib/site-packages/pythonwin/start_pythonwin.pyw new file mode 100644 index 000000000..0a1b2e60b --- /dev/null +++ b/myenv/Lib/site-packages/pythonwin/start_pythonwin.pyw @@ -0,0 +1,19 @@ +# A Python file that can be used to start Pythonwin, instead of using +# pythonwin.exe +import os +import sys + +import win32ui + +import pywin.framework.intpyapp # InteractivePythonApp() + +assert pywin.framework.intpyapp # not unused +# Pretend this script doesn't exist, or pythonwin tries to edit it +sys.argv[:] = sys.argv[1:] or [""] # like PySys_SetArgv(Ex) +if sys.path[0] not in ("", ".", os.getcwd()): + sys.path.insert(0, os.getcwd()) +# And bootstrap the app. +app = win32ui.GetApp() +if not app.InitInstance(): + # Run when not already handled by DDE + app.Run() diff --git a/myenv/Lib/site-packages/pythonwin/win32ui.pyd b/myenv/Lib/site-packages/pythonwin/win32ui.pyd new file mode 100644 index 000000000..488254bc7 Binary files /dev/null and b/myenv/Lib/site-packages/pythonwin/win32ui.pyd differ diff --git a/myenv/Lib/site-packages/pythonwin/win32uiole.pyd b/myenv/Lib/site-packages/pythonwin/win32uiole.pyd new file mode 100644 index 000000000..70ba78248 Binary files /dev/null and b/myenv/Lib/site-packages/pythonwin/win32uiole.pyd differ diff --git a/myenv/Lib/site-packages/pywin32.pth b/myenv/Lib/site-packages/pywin32.pth new file mode 100644 index 000000000..b57c49647 --- /dev/null +++ b/myenv/Lib/site-packages/pywin32.pth @@ -0,0 +1,7 @@ +# .pth file for the PyWin32 extensions +win32 +win32\lib +Pythonwin +# And some hackery to deal with environments where the post_install script +# isn't run. +import pywin32_bootstrap diff --git a/myenv/Lib/site-packages/pywin32.version.txt b/myenv/Lib/site-packages/pywin32.version.txt new file mode 100644 index 000000000..cd307095a --- /dev/null +++ b/myenv/Lib/site-packages/pywin32.version.txt @@ -0,0 +1 @@ +306 diff --git a/orders.db b/orders.db new file mode 100644 index 000000000..b5b23d272 Binary files /dev/null and b/orders.db differ