1__license__   = 'GPL v3'
2__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
3__docformat__ = 'restructuredtext en'
4
5'''
6Perform various initialization tasks.
7'''
8
9import locale, sys, os
10
11# Default translation is NOOP
12from polyglot.builtins import builtins
13builtins.__dict__['_'] = lambda s: s
14
15# For strings which belong in the translation tables, but which shouldn't be
16# immediately translated to the environment language
17builtins.__dict__['__'] = lambda s: s
18
19# For backwards compat with some third party plugins
20builtins.__dict__['dynamic_property'] = lambda func: func(None)
21
22from calibre.constants import iswindows, ismacos, islinux, DEBUG, isfreebsd
23
24
25def get_debug_executable():
26    exe_name = 'calibre-debug' + ('.exe' if iswindows else '')
27    if hasattr(sys, 'frameworks_dir'):
28        base = os.path.dirname(sys.frameworks_dir)
29        return [os.path.join(base, 'MacOS', exe_name)]
30    if getattr(sys, 'run_local', None):
31        return [sys.run_local, exe_name]
32    nearby = os.path.join(os.path.dirname(os.path.abspath(sys.executable)), exe_name)
33    if getattr(sys, 'frozen', False):
34        return [nearby]
35    exloc = getattr(sys, 'executables_location', None)
36    if exloc:
37        ans = os.path.join(exloc, exe_name)
38        if os.path.exists(ans):
39            return [ans]
40    if os.path.exists(nearby):
41        return [nearby]
42    return [exe_name]
43
44
45def initialize_calibre():
46    if hasattr(initialize_calibre, 'initialized'):
47        return
48    initialize_calibre.initialized = True
49
50    # Ensure that all temp files/dirs are created under a calibre tmp dir
51    from calibre.ptempfile import base_dir
52    try:
53        base_dir()
54    except OSError:
55        pass  # Ignore this error during startup, so we can show a better error message to the user later.
56
57    #
58    # Ensure that the max number of open files is at least 1024
59    if iswindows:
60        # See https://msdn.microsoft.com/en-us/library/6e3b887c.aspx
61        from calibre_extensions import winutil
62        winutil.setmaxstdio(max(1024, winutil.getmaxstdio()))
63    else:
64        import resource
65        soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
66        if soft < 1024:
67            try:
68                resource.setrlimit(resource.RLIMIT_NOFILE, (min(1024, hard), hard))
69            except Exception:
70                if DEBUG:
71                    import traceback
72                    traceback.print_exc()
73
74    #
75    # Fix multiprocessing
76    from multiprocessing import spawn, util
77
78    def get_command_line(**kwds):
79        prog = 'from multiprocessing.spawn import spawn_main; spawn_main(%s)'
80        prog %= ', '.join('%s=%r' % item for item in kwds.items())
81        return get_debug_executable() + ['--fix-multiprocessing', '--', prog]
82    spawn.get_command_line = get_command_line
83    orig_spawn_passfds = util.spawnv_passfds
84
85    def spawnv_passfds(path, args, passfds):
86        try:
87            idx = args.index('-c')
88        except ValueError:
89            return orig_spawn_passfds(args[0], args, passfds)
90        patched_args = get_debug_executable() + ['--fix-multiprocessing', '--'] + args[idx + 1:]
91        return orig_spawn_passfds(patched_args[0], patched_args, passfds)
92    util.spawnv_passfds = spawnv_passfds
93
94    #
95    # Setup resources
96    import calibre.utils.resources as resources
97    resources
98
99    #
100    # Setup translations
101    from calibre.utils.localization import set_translators
102
103    set_translators()
104
105    #
106    # Initialize locale
107    # Import string as we do not want locale specific
108    # string.whitespace/printable, on windows especially, this causes problems.
109    # Before the delay load optimizations, string was loaded before this point
110    # anyway, so we preserve the old behavior explicitly.
111    import string
112    string
113    try:
114        locale.setlocale(locale.LC_ALL, '')  # set the locale to the user's default locale
115    except:
116        dl = locale.getdefaultlocale()
117        try:
118            if dl:
119                locale.setlocale(locale.LC_ALL, dl[0])
120        except:
121            pass
122
123    builtins.__dict__['lopen'] = open  # legacy compatibility
124    from calibre.utils.icu import title_case, lower as icu_lower, upper as icu_upper
125    builtins.__dict__['icu_lower'] = icu_lower
126    builtins.__dict__['icu_upper'] = icu_upper
127    builtins.__dict__['icu_title'] = title_case
128
129    def connect_lambda(bound_signal, self, func, **kw):
130        import weakref
131        r = weakref.ref(self)
132        del self
133        num_args = func.__code__.co_argcount - 1
134        if num_args < 0:
135            raise TypeError('lambda must take at least one argument')
136
137        def slot(*args):
138            ctx = r()
139            if ctx is not None:
140                if len(args) != num_args:
141                    args = args[:num_args]
142                func(ctx, *args)
143
144        bound_signal.connect(slot, **kw)
145    builtins.__dict__['connect_lambda'] = connect_lambda
146
147    if islinux or ismacos or isfreebsd:
148        # Name all threads at the OS level created using the threading module, see
149        # http://bugs.python.org/issue15500
150        import threading
151        from calibre_extensions import speedup
152
153        orig_start = threading.Thread.start
154
155        def new_start(self):
156            orig_start(self)
157            try:
158                name = self.name
159                if not name or name.startswith('Thread-'):
160                    name = self.__class__.__name__
161                    if name == 'Thread':
162                        name = self.name
163                if name:
164                    if isinstance(name, str):
165                        name = name.encode('ascii', 'replace').decode('ascii')
166                    speedup.set_thread_name(name[:15])
167            except Exception:
168                pass  # Don't care about failure to set name
169        threading.Thread.start = new_start
170