codekingpro/portable-devtools
114k
1"""Manages the cache of generated Python code.2 3Description4 This file manages the cache of generated Python code. When run from the5 command line, it also provides a number of options for managing that cache.6 7Implementation8 Each typelib is generated into a filename of format "{guid}x{lcid}x{major}x{minor}.py"9 10 An external persistant dictionary maps from all known IIDs in all known type libraries11 to the type library itself.12 13 Thus, whenever Python code knows the IID of an object, it can find the IID, LCID and version of14 the type library which supports it. Given this information, it can find the Python module15 with the support.16 17 If necessary, this support can be generated on the fly.18 19Hacks, to do, etc20 Currently just uses a pickled dictionary, but should used some sort of indexed file.21 Maybe an OLE2 compound file, or a bsddb file?22"""23import glob24import os25import sys26from importlib import reload27 28import pythoncom29import pywintypes30import win32com31import win32com.client32 33from . import CLSIDToClass34 35bForDemandDefault = 0 # Default value of bForDemand - toggle this to change the world - see also makepy.py36 37# The global dictionary38clsidToTypelib = {}39 40# If we have a different version of the typelib generated, this41# maps the "requested version" to the "generated version".42versionRedirectMap = {}43 44# There is no reason we *must* be readonly in a .zip, but we are now,45# Rather than check for ".zip" or other tricks, PEP302 defines46# a "__loader__" attribute, so we use that.47# (Later, it may become necessary to check if the __loader__ can update files,48# as a .zip loader potentially could - but punt all that until a need arises)49is_readonly = is_zip = hasattr(win32com, "__loader__") and hasattr(50 win32com.__loader__, "archive"51)52 53# A dictionary of ITypeLibrary objects for demand generation explicitly handed to us54# Keyed by usual clsid, lcid, major, minor55demandGeneratedTypeLibraries = {}56 57import pickle as pickle58 59 60def __init__():61 # Initialize the module. Called once explicitly at module import below.62 try:63 _LoadDicts()64 except IOError:65 Rebuild()66 67 68pickleVersion = 169 70 71def _SaveDicts():72 if is_readonly:73 raise RuntimeError(74 "Trying to write to a readonly gencache ('%s')!" % win32com.__gen_path__75 )76 f = open(os.path.join(GetGeneratePath(), "dicts.dat"), "wb")77 try:78 p = pickle.Pickler(f)79 p.dump(pickleVersion)80 p.dump(clsidToTypelib)81 finally:82 f.close()83 84 85def _LoadDicts():86 # Load the dictionary from a .zip file if that is where we live.87 if is_zip:88 import io as io89 90 loader = win32com.__loader__91 arc_path = loader.archive92 dicts_path = os.path.join(win32com.__gen_path__, "dicts.dat")93 if dicts_path.startswith(arc_path):94 dicts_path = dicts_path[len(arc_path) + 1 :]95 else:96 # Hm. See below.97 return98 try:99 data = loader.get_data(dicts_path)100 except AttributeError:101 # The __loader__ has no get_data method. See below.102 return103 except IOError:104 # Our gencache is in a .zip file (and almost certainly readonly)105 # but no dicts file. That actually needn't be fatal for a frozen106 # application. Assuming they call "EnsureModule" with the same107 # typelib IDs they have been frozen with, that EnsureModule will108 # correctly re-build the dicts on the fly. However, objects that109 # rely on the gencache but have not done an EnsureModule will110 # fail (but their apps are likely to fail running from source111 # with a clean gencache anyway, as then they would be getting112 # Dynamic objects until the cache is built - so the best answer113 # for these apps is to call EnsureModule, rather than freezing114 # the dict)115 return116 f = io.BytesIO(data)117 else:118 # NOTE: IOError on file open must be caught by caller.119 f = open(os.path.join(win32com.__gen_path__, "dicts.dat"), "rb")120 try:121 p = pickle.Unpickler(f)122 version = p.load()123 global clsidToTypelib124 clsidToTypelib = p.load()125 versionRedirectMap.clear()126 finally:127 f.close()128 129 130def GetGeneratedFileName(clsid, lcid, major, minor):131 """Given the clsid, lcid, major and minor for a type lib, return132 the file name (no extension) providing this support.133 """134 return str(clsid).upper()[1:-1] + "x%sx%sx%s" % (lcid, major, minor)135 136 137def SplitGeneratedFileName(fname):138 """Reverse of GetGeneratedFileName()"""139 return tuple(fname.split("x", 4))140 141 142def GetGeneratePath():143 """Returns the name of the path to generate to.144 Checks the directory is OK.145 """146 assert not is_readonly, "Why do you want the genpath for a readonly store?"147 try:148 os.makedirs(win32com.__gen_path__)149 # os.mkdir(win32com.__gen_path__)150 except os.error:151 pass152 try:153 fname = os.path.join(win32com.__gen_path__, "__init__.py")154 os.stat(fname)155 except os.error:156 f = open(fname, "w")157 f.write(158 "# Generated file - this directory may be deleted to reset the COM cache...\n"159 )160 f.write("import win32com\n")161 f.write(162 "if __path__[:-1] != win32com.__gen_path__: __path__.append(win32com.__gen_path__)\n"163 )164 f.close()165 166 return win32com.__gen_path__167 168 169#170# The helpers for win32com.client.Dispatch and OCX clients.171#172def GetClassForProgID(progid):173 """Get a Python class for a Program ID174 175 Given a Program ID, return a Python class which wraps the COM object176 177 Returns the Python class, or None if no module is available.178 179 Params180 progid -- A COM ProgramID or IID (eg, "Word.Application")181 """182 clsid = pywintypes.IID(progid) # This auto-converts named to IDs.183 return GetClassForCLSID(clsid)184 185 186def GetClassForCLSID(clsid):187 """Get a Python class for a CLSID188 189 Given a CLSID, return a Python class which wraps the COM object190 191 Returns the Python class, or None if no module is available.192 193 Params194 clsid -- A COM CLSID (or string repr of one)195 """196 # first, take a short-cut - we may already have generated support ready-to-roll.197 clsid = str(clsid)198 if CLSIDToClass.HasClass(clsid):199 return CLSIDToClass.GetClass(clsid)200 mod = GetModuleForCLSID(clsid)201 if mod is None:202 return None203 try:204 return CLSIDToClass.GetClass(clsid)205 except KeyError:206 return None207 208 209def GetModuleForProgID(progid):210 """Get a Python module for a Program ID211 212 Given a Program ID, return a Python module which contains the213 class which wraps the COM object.214 215 Returns the Python module, or None if no module is available.216 217 Params218 progid -- A COM ProgramID or IID (eg, "Word.Application")219 """220 try:221 iid = pywintypes.IID(progid)222 except pywintypes.com_error:223 return None224 return GetModuleForCLSID(iid)225 226 227def GetModuleForCLSID(clsid):228 """Get a Python module for a CLSID229 230 Given a CLSID, return a Python module which contains the231 class which wraps the COM object.232 233 Returns the Python module, or None if no module is available.234 235 Params236 progid -- A COM CLSID (ie, not the description)237 """238 clsid_str = str(clsid)239 try:240 typelibCLSID, lcid, major, minor = clsidToTypelib[clsid_str]241 except KeyError:242 return None243 244 try:245 mod = GetModuleForTypelib(typelibCLSID, lcid, major, minor)246 except ImportError:247 mod = None248 if mod is not None:249 sub_mod = mod.CLSIDToPackageMap.get(clsid_str)250 if sub_mod is None:251 sub_mod = mod.VTablesToPackageMap.get(clsid_str)252 if sub_mod is not None:253 sub_mod_name = mod.__name__ + "." + sub_mod254 try:255 __import__(sub_mod_name)256 except ImportError:257 info = typelibCLSID, lcid, major, minor258 # Force the generation. If this typelibrary has explicitly been added,259 # use it (it may not be registered, causing a lookup by clsid to fail)260 if info in demandGeneratedTypeLibraries:261 info = demandGeneratedTypeLibraries[info]262 from . import makepy263 264 makepy.GenerateChildFromTypeLibSpec(sub_mod, info)265 # Generate does an import...266 mod = sys.modules[sub_mod_name]267 return mod268 269 270def GetModuleForTypelib(typelibCLSID, lcid, major, minor):271 """Get a Python module for a type library ID272 273 Given the CLSID of a typelibrary, return an imported Python module,274 else None275 276 Params277 typelibCLSID -- IID of the type library.278 major -- Integer major version.279 minor -- Integer minor version280 lcid -- Integer LCID for the library.281 """282 modName = GetGeneratedFileName(typelibCLSID, lcid, major, minor)283 mod = _GetModule(modName)284 # If the import worked, it doesn't mean we have actually added this285 # module to our cache though - check that here.286 if "_in_gencache_" not in mod.__dict__:287 AddModuleToCache(typelibCLSID, lcid, major, minor)288 assert "_in_gencache_" in mod.__dict__289 return mod290 291 292def MakeModuleForTypelib(293 typelibCLSID,294 lcid,295 major,296 minor,297 progressInstance=None,298 bForDemand=bForDemandDefault,299 bBuildHidden=1,300):301 """Generate support for a type library.302 303 Given the IID, LCID and version information for a type library, generate304 and import the necessary support files.305 306 Returns the Python module. No exceptions are caught.307 308 Params309 typelibCLSID -- IID of the type library.310 major -- Integer major version.311 minor -- Integer minor version.312 lcid -- Integer LCID for the library.313 progressInstance -- Instance to use as progress indicator, or None to314 use the GUI progress bar.315 """316 from . import makepy317 318 makepy.GenerateFromTypeLibSpec(319 (typelibCLSID, lcid, major, minor),320 progressInstance=progressInstance,321 bForDemand=bForDemand,322 bBuildHidden=bBuildHidden,323 )324 return GetModuleForTypelib(typelibCLSID, lcid, major, minor)325 326 327def MakeModuleForTypelibInterface(328 typelib_ob, progressInstance=None, bForDemand=bForDemandDefault, bBuildHidden=1329):330 """Generate support for a type library.331 332 Given a PyITypeLib interface generate and import the necessary support files. This is useful333 for getting makepy support for a typelibrary that is not registered - the caller can locate334 and load the type library itself, rather than relying on COM to find it.335 336 Returns the Python module.337 338 Params339 typelib_ob -- The type library itself340 progressInstance -- Instance to use as progress indicator, or None to341 use the GUI progress bar.342 """343 from . import makepy344 345 try:346 makepy.GenerateFromTypeLibSpec(347 typelib_ob,348 progressInstance=progressInstance,349 bForDemand=bForDemandDefault,350 bBuildHidden=bBuildHidden,351 )352 except pywintypes.com_error:353 return None354 tla = typelib_ob.GetLibAttr()355 guid = tla[0]356 lcid = tla[1]357 major = tla[3]358 minor = tla[4]359 return GetModuleForTypelib(guid, lcid, major, minor)360 361 362def EnsureModuleForTypelibInterface(363 typelib_ob, progressInstance=None, bForDemand=bForDemandDefault, bBuildHidden=1364):365 """Check we have support for a type library, generating if not.366 367 Given a PyITypeLib interface generate and import the necessary368 support files if necessary. This is useful for getting makepy support369 for a typelibrary that is not registered - the caller can locate and370 load the type library itself, rather than relying on COM to find it.371 372 Returns the Python module.373 374 Params375 typelib_ob -- The type library itself376 progressInstance -- Instance to use as progress indicator, or None to377 use the GUI progress bar.378 """379 tla = typelib_ob.GetLibAttr()380 guid = tla[0]381 lcid = tla[1]382 major = tla[3]383 minor = tla[4]384 385 # If demand generated, save the typelib interface away for later use386 if bForDemand:387 demandGeneratedTypeLibraries[(str(guid), lcid, major, minor)] = typelib_ob388 389 try:390 return GetModuleForTypelib(guid, lcid, major, minor)391 except ImportError:392 pass393 # Generate it.394 return MakeModuleForTypelibInterface(395 typelib_ob, progressInstance, bForDemand, bBuildHidden396 )397 398 399def ForgetAboutTypelibInterface(typelib_ob):400 """Drop any references to a typelib previously added with EnsureModuleForTypelibInterface and forDemand"""401 tla = typelib_ob.GetLibAttr()402 guid = tla[0]403 lcid = tla[1]404 major = tla[3]405 minor = tla[4]406 info = str(guid), lcid, major, minor407 try:408 del demandGeneratedTypeLibraries[info]409 except KeyError:410 # Not worth raising an exception - maybe they dont know we only remember for demand generated, etc.411 print(412 "ForgetAboutTypelibInterface:: Warning - type library with info %s is not being remembered!"413 % (info,)414 )415 # and drop any version redirects to it416 for key, val in list(versionRedirectMap.items()):417 if val == info:418 del versionRedirectMap[key]419 420 421def EnsureModule(422 typelibCLSID,423 lcid,424 major,425 minor,426 progressInstance=None,427 bValidateFile=not is_readonly,428 bForDemand=bForDemandDefault,429 bBuildHidden=1,430):431 """Ensure Python support is loaded for a type library, generating if necessary.432 433 Given the IID, LCID and version information for a type library, check and if434 necessary (re)generate, then import the necessary support files. If we regenerate the file, there435 is no way to totally snuff out all instances of the old module in Python, and thus we will regenerate the file more than necessary,436 unless makepy/genpy is modified accordingly.437 438 439 Returns the Python module. No exceptions are caught during the generate process.440 441 Params442 typelibCLSID -- IID of the type library.443 major -- Integer major version.444 minor -- Integer minor version445 lcid -- Integer LCID for the library.446 progressInstance -- Instance to use as progress indicator, or None to447 use the GUI progress bar.448 bValidateFile -- Whether or not to perform cache validation or not449 bForDemand -- Should a complete generation happen now, or on demand?450 bBuildHidden -- Should hidden members/attributes etc be generated?451 """452 bReloadNeeded = 0453 try:454 try:455 module = GetModuleForTypelib(typelibCLSID, lcid, major, minor)456 except ImportError:457 # If we get an ImportError458 # We may still find a valid cache file under a different MinorVersion #459 # (which windows will search out for us)460 # print "Loading reg typelib", typelibCLSID, major, minor, lcid461 module = None462 try:463 tlbAttr = pythoncom.LoadRegTypeLib(464 typelibCLSID, major, minor, lcid465 ).GetLibAttr()466 # if the above line doesn't throw a pythoncom.com_error, check if467 # it is actually a different lib than we requested, and if so, suck it in468 if tlbAttr[1] != lcid or tlbAttr[4] != minor:469 # print "Trying 2nd minor #", tlbAttr[1], tlbAttr[3], tlbAttr[4]470 try:471 module = GetModuleForTypelib(472 typelibCLSID, tlbAttr[1], tlbAttr[3], tlbAttr[4]473 )474 except ImportError:475 # We don't have a module, but we do have a better minor476 # version - remember that.477 minor = tlbAttr[4]478 # else module remains None479 except pythoncom.com_error:480 # couldn't load any typelib - mod remains None481 pass482 if module is not None and bValidateFile:483 assert not is_readonly, "Can't validate in a read-only gencache"484 try:485 typLibPath = pythoncom.QueryPathOfRegTypeLib(486 typelibCLSID, major, minor, lcid487 )488 # windows seems to add an extra \0 (via the underlying BSTR)489 # The mainwin toolkit does not add this erroneous \0490 if typLibPath[-1] == "\0":491 typLibPath = typLibPath[:-1]492 suf = getattr(os.path, "supports_unicode_filenames", 0)493 if not suf:494 # can't pass unicode filenames directly - convert495 try:496 typLibPath = typLibPath.encode(sys.getfilesystemencoding())497 except AttributeError: # no sys.getfilesystemencoding498 typLibPath = str(typLibPath)499 tlbAttributes = pythoncom.LoadRegTypeLib(500 typelibCLSID, major, minor, lcid501 ).GetLibAttr()502 except pythoncom.com_error:503 # We have a module, but no type lib - we should still504 # run with what we have though - the typelib may not be505 # deployed here.506 bValidateFile = 0507 if module is not None and bValidateFile:508 assert not is_readonly, "Can't validate in a read-only gencache"509 filePathPrefix = "%s\\%s" % (510 GetGeneratePath(),511 GetGeneratedFileName(typelibCLSID, lcid, major, minor),512 )513 filePath = filePathPrefix + ".py"514 filePathPyc = filePathPrefix + ".py"515 if __debug__:516 filePathPyc = filePathPyc + "c"517 else:518 filePathPyc = filePathPyc + "o"519 # Verify that type library is up to date.520 # If we have a differing MinorVersion or genpy has bumped versions, update the file521 from . import genpy522 523 if (524 module.MinorVersion != tlbAttributes[4]525 or genpy.makepy_version != module.makepy_version526 ):527 # print "Version skew: %d, %d" % (module.MinorVersion, tlbAttributes[4])528 # try to erase the bad file from the cache529 try:530 os.unlink(filePath)531 except os.error:532 pass533 try:534 os.unlink(filePathPyc)535 except os.error:536 pass537 if os.path.isdir(filePathPrefix):538 import shutil539 540 shutil.rmtree(filePathPrefix)541 minor = tlbAttributes[4]542 module = None543 bReloadNeeded = 1544 else:545 minor = module.MinorVersion546 filePathPrefix = "%s\\%s" % (547 GetGeneratePath(),548 GetGeneratedFileName(typelibCLSID, lcid, major, minor),549 )550 filePath = filePathPrefix + ".py"551 filePathPyc = filePathPrefix + ".pyc"552 # print "Trying py stat: ", filePath553 fModTimeSet = 0554 try:555 pyModTime = os.stat(filePath)[8]556 fModTimeSet = 1557 except os.error as e:558 # If .py file fails, try .pyc file559 # print "Trying pyc stat", filePathPyc560 try:561 pyModTime = os.stat(filePathPyc)[8]562 fModTimeSet = 1563 except os.error as e:564 pass565 # print "Trying stat typelib", pyModTime566 # print str(typLibPath)567 typLibModTime = os.stat(typLibPath)[8]568 if fModTimeSet and (typLibModTime > pyModTime):569 bReloadNeeded = 1570 module = None571 except (ImportError, os.error):572 module = None573 if module is None:574 # We need to build an item. If we are in a read-only cache, we575 # can't/don't want to do this - so before giving up, check for576 # a different minor version in our cache - according to COM, this is OK577 if is_readonly:578 key = str(typelibCLSID), lcid, major, minor579 # If we have been asked before, get last result.580 try:581 return versionRedirectMap[key]582 except KeyError:583 pass584 # Find other candidates.585 items = []586 for desc in GetGeneratedInfos():587 if key[0] == desc[0] and key[1] == desc[1] and key[2] == desc[2]:588 items.append(desc)589 if items:590 # Items are all identical, except for last tuple element591 # We want the latest minor version we have - so just sort and grab last592 items.sort()593 new_minor = items[-1][3]594 ret = GetModuleForTypelib(typelibCLSID, lcid, major, new_minor)595 else:596 ret = None597 # remember and return598 versionRedirectMap[key] = ret599 return ret600 # print "Rebuilding: ", major, minor601 module = MakeModuleForTypelib(602 typelibCLSID,603 lcid,604 major,605 minor,606 progressInstance,607 bForDemand=bForDemand,608 bBuildHidden=bBuildHidden,609 )610 # If we replaced something, reload it611 if bReloadNeeded:612 module = reload(module)613 AddModuleToCache(typelibCLSID, lcid, major, minor)614 return module615 616 617def EnsureDispatch(618 prog_id, bForDemand=1619): # New fn, so we default the new demand feature to on!620 """Given a COM prog_id, return an object that is using makepy support, building if necessary"""621 disp = win32com.client.Dispatch(prog_id)622 if not disp.__dict__.get("CLSID"): # Eeek - no makepy support - try and build it.623 try:624 ti = disp._oleobj_.GetTypeInfo()625 disp_clsid = ti.GetTypeAttr()[0]626 tlb, index = ti.GetContainingTypeLib()627 tla = tlb.GetLibAttr()628 mod = EnsureModule(tla[0], tla[1], tla[3], tla[4], bForDemand=bForDemand)629 GetModuleForCLSID(disp_clsid)630 # Get the class from the module.631 from . import CLSIDToClass632 633 disp_class = CLSIDToClass.GetClass(str(disp_clsid))634 disp = disp_class(disp._oleobj_)635 except pythoncom.com_error:636 raise TypeError(637 "This COM object can not automate the makepy process - please run makepy manually for this object"638 )639 return disp640 641 642def AddModuleToCache(643 typelibclsid, lcid, major, minor, verbose=1, bFlushNow=not is_readonly644):645 """Add a newly generated file to the cache dictionary."""646 fname = GetGeneratedFileName(typelibclsid, lcid, major, minor)647 mod = _GetModule(fname)648 # if mod._in_gencache_ is already true, then we are reloading this649 # module - this doesn't mean anything special though!650 mod._in_gencache_ = 1651 info = str(typelibclsid), lcid, major, minor652 dict_modified = False653 654 def SetTypelibForAllClsids(dict):655 nonlocal dict_modified656 for clsid, cls in dict.items():657 if clsidToTypelib.get(clsid) != info:658 clsidToTypelib[clsid] = info659 dict_modified = True660 661 SetTypelibForAllClsids(mod.CLSIDToClassMap)662 SetTypelibForAllClsids(mod.CLSIDToPackageMap)663 SetTypelibForAllClsids(mod.VTablesToClassMap)664 SetTypelibForAllClsids(mod.VTablesToPackageMap)665 666 # If this lib was previously redirected, drop it667 if info in versionRedirectMap:668 del versionRedirectMap[info]669 if bFlushNow and dict_modified:670 _SaveDicts()671 672 673def GetGeneratedInfos():674 zip_pos = win32com.__gen_path__.find(".zip\\")675 if zip_pos >= 0:676 import zipfile677 678 zip_file = win32com.__gen_path__[: zip_pos + 4]679 zip_path = win32com.__gen_path__[zip_pos + 5 :].replace("\\", "/")680 zf = zipfile.ZipFile(zip_file)681 infos = {}682 for n in zf.namelist():683 if not n.startswith(zip_path):684 continue685 base = n[len(zip_path) + 1 :].split("/")[0]686 try:687 iid, lcid, major, minor = base.split("x")688 lcid = int(lcid)689 major = int(major)690 minor = int(minor)691 iid = pywintypes.IID("{" + iid + "}")692 except ValueError:693 continue694 except pywintypes.com_error:695 # invalid IID696 continue697 infos[(iid, lcid, major, minor)] = 1698 zf.close()699 return list(infos.keys())700 else:701 # on the file system702 files = glob.glob(win32com.__gen_path__ + "\\*")703 ret = []704 for file in files:705 if not os.path.isdir(file) and not os.path.splitext(file)[1] == ".py":706 continue707 name = os.path.splitext(os.path.split(file)[1])[0]708 try:709 iid, lcid, major, minor = name.split("x")710 iid = pywintypes.IID("{" + iid + "}")711 lcid = int(lcid)712 major = int(major)713 minor = int(minor)714 except ValueError:715 continue716 except pywintypes.com_error:717 # invalid IID718 continue719 ret.append((iid, lcid, major, minor))720 return ret721 722 723def _GetModule(fname):724 """Given the name of a module in the gen_py directory, import and return it."""725 mod_name = "win32com.gen_py.%s" % fname726 mod = __import__(mod_name)727 return sys.modules[mod_name]728 729 730def Rebuild(verbose=1):731 """Rebuild the cache indexes from the file system."""732 clsidToTypelib.clear()733 infos = GetGeneratedInfos()734 if verbose and len(infos): # Dont bother reporting this when directory is empty!735 print("Rebuilding cache of generated files for COM support...")736 for info in infos:737 iid, lcid, major, minor = info738 if verbose:739 print("Checking", GetGeneratedFileName(*info))740 try:741 AddModuleToCache(iid, lcid, major, minor, verbose, 0)742 except:743 print(744 "Could not add module %s - %s: %s"745 % (info, sys.exc_info()[0], sys.exc_info()[1])746 )747 if verbose and len(infos): # Dont bother reporting this when directory is empty!748 print("Done.")749 _SaveDicts()750 751 752def _Dump():753 print("Cache is in directory", win32com.__gen_path__)754 # Build a unique dir755 d = {}756 for clsid, (typelibCLSID, lcid, major, minor) in clsidToTypelib.items():757 d[typelibCLSID, lcid, major, minor] = None758 for typelibCLSID, lcid, major, minor in d.keys():759 mod = GetModuleForTypelib(typelibCLSID, lcid, major, minor)760 print("%s - %s" % (mod.__doc__, typelibCLSID))761 762 763# Boot up764__init__()765 766 767def usage():768 usageString = """\769 Usage: gencache [-q] [-d] [-r]770 771 -q - Quiet772 -d - Dump the cache (typelibrary description and filename).773 -r - Rebuild the cache dictionary from the existing .py files774 """775 print(usageString)776 sys.exit(1)777 778 779if __name__ == "__main__":780 import getopt781 782 try:783 opts, args = getopt.getopt(sys.argv[1:], "qrd")784 except getopt.error as message:785 print(message)786 usage()787 788 # we only have options - complain about real args, or none at all!789 if len(sys.argv) == 1 or args:790 print(usage())791 792 verbose = 1793 for opt, val in opts:794 if opt == "-d": # Dump795 _Dump()796 if opt == "-r":797 Rebuild(verbose)798 if opt == "-q":799 verbose = 0800 