-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathgeneral.py
109 lines (94 loc) · 3.89 KB
/
general.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# -*- coding: utf-8 -*-
"""
Created on Thu Aug 20 18:44:38 2015
@author: Winand
"""
SOURCEDIR, DEF_MODULE = "source", "default"
macro_tree, modules = {}, {}
comobj_cache = {}
from win32com.client import gencache
import pythoncom, string, sys, datetime, builtins, threading
from functools import wraps
import context
import cProfile, pstats, io #Profiling
from threaded_ui import app
run_lock = threading.Semaphore(2) #for macro interruption
COL = {} #dict of column names
for i in string.ascii_uppercase:
COL[i] = ord(i)-ord("A")+1
COL["A"+i] = 26+ord(i)-ord("A")+1
def print(*args, **kwargs):
"print with timestamp"
builtins.print(datetime.datetime.now().strftime("%M:%S|"), *args, **kwargs)
def optional_arguments(f):
"Allows to use any decorator with or w/o arguments \
http://stackoverflow.com/a/14412901/1119602"
@wraps(f)
def new_dec(*args, **kwargs):
if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
return f(args[0])
else: return lambda realf: f(realf, *args, **kwargs)
return new_dec
@optional_arguments
def macro(func, for_=context.Office):
if func.__module__.startswith(SOURCEDIR+"."):
module = func.__module__[len(SOURCEDIR+"."):]
else: module = func.__module__
if module not in macro_tree:
macro_tree[module] = {}
if func.__name__ not in macro_tree[module]: #in case of duplicate macro names
macro_tree[module][func.__name__] = for_
@wraps(func)
def wrapper(doc):
doc_obj = getOpenedFileObject(doc)
if doc_obj:
context.context(doc_obj, modules[module])
try:
with run_lock:
# with Profile():
ret = func()
while not run_lock._value: pass #prevent interruption outside try block
return ret
except KeyboardInterrupt:
print("Macro '%s' interrupted"%func.__name__)
except Exception as e:
frame = sys.exc_info()[2].tb_next
if not frame: frame = sys.exc_info()[2]
print("Exception in macro '%s': %s[%s:%d] %s" %
(func.__name__, type(e).__name__, module, frame.tb_lineno, e))
showConsole() #Show exception to user
finally:
try: context.App.ScreenUpdating = True #Turn back updating!
except: print("Failed to turn on screen updating!")
else: print("Opened document '%s' not found"%doc)
return wrapper
def getOpenedFileObject(name):
if name in comobj_cache:
try:
#http://stackoverflow.com/questions/3500503/check-com-interface-still-alive
o = comobj_cache[name]
o._oleobj_.GetTypeInfoCount()
return o
except: del comobj_cache[name]
ctx, rot = pythoncom.CreateBindCtx(0), pythoncom.GetRunningObjectTable()
for i in rot:
if i.GetDisplayName(ctx, None) == name:
comobj_cache[name] = gencache.EnsureDispatch(
rot.GetObject(i).QueryInterface(pythoncom.IID_IDispatch))
return comobj_cache[name]
def getMacroList(app):
l = [f if m==DEF_MODULE else m+"."+f for m in macro_tree for f in macro_tree[m] if macro_tree[m][f] in (app, context.Office)]
return sorted(l, key=lambda s: s.lower())
class Profile():
def __enter__(self):
self.pr = cProfile.Profile()
self.pr.enable()
def __exit__(self, exc_type, exc_value, traceback):
self.pr.disable()
s = io.StringIO()
pstats.Stats(self.pr, stream=s).sort_stats('cumulative').print_stats()
print(s.getvalue())
def showConsole():
app().form.showWindow(console=True)
context.macro = macro #so macro can be imported from context
context.print = lambda *args, **kwargs: print(">", *args, **kwargs) #macro prints are marked with ">" sign