forked from albertz/music-player
/
main.py
executable file
·198 lines (166 loc) · 6.2 KB
/
main.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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
#!/usr/bin/env python
# MusicPlayer, https://github.com/albertz/music-player
# Copyright (c) 2012, Albert Zeyer, www.az2000.de
# All rights reserved.
# This code is under the 2-clause BSD license, see License.txt in the root directory of this project.
# This is set to True when the startup was successful.
successStartup = False
import better_exchook
better_exchook.install()
import sys
try:
import faulthandler
# Only enable if we don't execute from within the MusicPlayer binary.
# The MusicPlayer binary will setup its own crash handlers.
# Import the `faulthandler` module though, we might use it.
if not getattr(sys, "MusicPlayerBin", None):
faulthandler.enable(all_threads=True)
except ImportError:
print "note: faulthandler module not available"
faulthandler = None
# Do this early to do some option parsing and maybe special handling.
import appinfo
# Early check for "--profile".
# Enable profiling.
if __name__ == '__main__' and appinfo.args.profile:
# No try/except. If requested and it fails -> exit.
import cProfile
profiler = cProfile.Profile()
# Note that this should be done outside of main() because
# otherwise it would not really profile the main() itself.
profiler.enable()
else:
profiler = None
# Early check for "--addsyspythonpaths".
# Add System Python paths.
if __name__ == '__main__' and appinfo.args.addsyspythonpaths:
import debug
debug.addSysPythonPath()
def handleApplicationInit():
import State
if not appinfo.args.nomodstartup:
for m in State.modules: m.start()
else:
# In some cases, we at least need some modules. Start only those.
if appinfo.args.shell:
State.getModule("stdinconsole").start()
global successStartup
successStartup = True
def main():
import sys, os
from pprint import pprint
# Early check for forked process.
if appinfo.args.forkExecProc:
# Only import utils now for this case.
# Otherwise, I want "--pyshell" to be without utils loaded.
import utils
utils.ExecingProcess.checkExec()
# Early check for "--pyshell".
# This is a simple debug shell where we don't load anything.
if appinfo.args.pyshell:
better_exchook.simple_debug_shell({}, {})
raise SystemExit
# Early check for "--pyexec".
# This is a simple Python execution where we don't load anything.
if appinfo.args.pyexec:
sourcecode = appinfo.args.pyexec[0]
exec(compile(sourcecode, "<pyexec>", "exec"))
raise SystemExit
import utils
import time
print "MusicPlayer", appinfo.version, "from", appinfo.buildTime, "git-ref", appinfo.gitRef[:10], "on", appinfo.platform, "(%s)" % sys.platform
print "startup on", utils.formatDate(time.time())
utils.setCurThreadName("Python main")
try:
# Hack: Make the `__main__` module also accessible as `main`.
mainmod = sys.modules["__main__"]
sys.modules.setdefault("main", mainmod)
del mainmod
except Exception:
sys.excepthook(*sys.exc_info())
# doesn't matter, continue
# Import PyObjC here. This is because the first import of PyObjC *must* be
# in the main thread. Otherwise, the NSAutoreleasePool created automatically
# by PyObjC on the first import would be released at exit by the main thread
# which would crash (because it was created in a different thread).
# http://pyobjc.sourceforge.net/documentation/pyobjc-core/intro.html
objc, AppKit = None, None
try:
import objc
except Exception:
if sys.platform == "darwin":
print "Error while importing objc"
sys.excepthook(*sys.exc_info())
# Otherwise it doesn't matter.
try:
# Seems that the `objc` module is not enough. Without `AppKit`,
# I still get a lot of
# __NSAutoreleaseNoPool(): ... autoreleased with no pool in place - just leaking
# errors.
if objc:
import AppKit
except Exception:
# Print error in any case, also ImportError, because we would expect that this works.
print "Error while importing AppKit"
sys.excepthook(*sys.exc_info())
# Import core module here. This is mostly as an early error check.
try:
import musicplayer
except Exception:
print "Error while importing core module! This is fatal."
sys.excepthook(*sys.exc_info())
print "Environment:"
pprint(os.environ)
raise
# Import gui module here. Again, mostly as an early error check.
# If there is no gui, the module should still load and provide
# dummy functions where appropriate.
import gui
# Default quit handling.
# Note that this is executed after `threading._shutdown`, i.e. after
# all non-daemon threads have finished.
import atexit
atexit.register(gui.handleApplicationQuit)
# Import some core modules. They propagate themselves to other
# subsystems, like GUI.
# XXX: Maybe move all this to `State` module?
import State
import Preferences
import Search
import SongEdit
# This will overtake the main loop and raise SystemExit at its end,
# or never return.
# It also just might do nothing.
gui.main()
# If we continue here, we can setup our own main loop.
# We have no GUI. Continue with some simple console control handling.
import stdinconsole
handleApplicationInit()
# Note on quit behavior: Simply iterating state.updates
# and waiting for its end does not work because we would
# not interrupt on signals, e.g. KeyboardInterrupt.
# It is also not possible (in general) to catch
# signals from other threads, thus we have to do it here.
# time.sleep() is a good way to wait for signals.
# However, we use stdinconsole.readNextInput() because
# there is simply no way to have os.read() in another thread
# and to be able to interrupt that from here (the main thread).
# In other threads: thread.interrupt_main() does not work
# for time.sleep() (or at least it will not interrupt the sleep).
# os.kill(0, signal.SIGINT) works, though.
# To interrupt/stop all threads:
# signal.set_wakeup_fd(sys.stdin.fileno()) also does not really
# work to interrupt the stdin thread, probably because stdin is
# not non-blocking.
# Every thread must only wait on a OnRequestQueue which registers
# itself in its thread. We cancelAll() here already the main queue
# (state.updates) and in Module.stop(), we also cancel any custom
# queue.
while True:
try: stdinconsole.readNextInput() # wait for KeyboardInterrupt
except BaseException, e:
State.state.updates.put((e, (), {}))
State.state.updates.cancelAll()
break
if __name__ == '__main__':
main()