~ubuntu-branches/debian/experimental/nuitka/experimental

« back to all changes in this revision

Viewing changes to nuitka/Importing.py

  • Committer: Package Import Robot
  • Author(s): Kay Hayen
  • Date: 2015-04-06 17:20:44 UTC
  • mfrom: (1.1.37)
  • Revision ID: package-import@ubuntu.com-20150406172044-grhy9sk7g0whu2ag
Tags: 0.5.12+ds-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#     Copyright 2015, Kay Hayen, mailto:kay.hayen@gmail.com
2
 
#
3
 
#     Part of "Nuitka", an optimizing Python compiler that is compatible and
4
 
#     integrates with CPython, but also works on its own.
5
 
#
6
 
#     Licensed under the Apache License, Version 2.0 (the "License");
7
 
#     you may not use this file except in compliance with the License.
8
 
#     You may obtain a copy of the License at
9
 
#
10
 
#        http://www.apache.org/licenses/LICENSE-2.0
11
 
#
12
 
#     Unless required by applicable law or agreed to in writing, software
13
 
#     distributed under the License is distributed on an "AS IS" BASIS,
14
 
#     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 
#     See the License for the specific language governing permissions and
16
 
#     limitations under the License.
17
 
#
18
 
""" Locating modules and package source on disk.
19
 
 
20
 
The actual import of a module would already execute code that changes things.
21
 
Imagine a module that does "os.system()", it will be done. People often connect
22
 
to databases, and these kind of things, at import time. Not a good style, but
23
 
it's being done.
24
 
 
25
 
Therefore CPython exhibits the interfaces in an "imp" module in standard
26
 
library, which one can use those to know ahead of time, what file import would
27
 
load. For us unfortunately there is nothing in CPython that gives the fully
28
 
compatible functionality we need for packages and search paths exactly like
29
 
CPython does, so we implement here a multiple step search process that is
30
 
compatible.
31
 
 
32
 
This approach is much safer of course and there is no loss. To determine if it's
33
 
from the standard library, we can abuse the attribute "__file__" of the "os"
34
 
module like it's done in "isStandardLibraryPath" of this module.
35
 
 
36
 
"""
37
 
 
38
 
from __future__ import print_function
39
 
 
40
 
import imp
41
 
import os
42
 
import sys
43
 
from logging import warning
44
 
 
45
 
from . import Utils, oset
46
 
 
47
 
_debug_module_finding = False
48
 
 
49
 
warned_about = set()
50
 
 
51
 
# Directory where the main script lives. Should attempt to import from there.
52
 
main_path = None
53
 
 
54
 
def setMainScriptDirectory(main_dir):
55
 
    """ Initialize the main script directory.
56
 
 
57
 
        We use this as part of the search path for modules.
58
 
    """
59
 
    # We need to set this from the outside, pylint: disable=W0603
60
 
 
61
 
    global main_path
62
 
    main_path = main_dir
63
 
 
64
 
def isPackageDir(dirname):
65
 
    """ Decide if a directory is a package.
66
 
 
67
 
        Before Python3.3 it's required to have a "__init__.py" file, but then
68
 
        it became impossible to decide.
69
 
    """
70
 
 
71
 
    return Utils.isDir(dirname) and \
72
 
           (
73
 
               Utils.python_version >= 330 or
74
 
               Utils.isFile(Utils.joinpath(dirname, "__init__.py"))
75
 
           )
76
 
 
77
 
def findModule(source_ref, module_name, parent_package, level, warn):
78
 
    """ Find a module with given package name as parent.
79
 
 
80
 
        The package name can be None of course. Level is the same
81
 
        as with "__import__" built-in. Warnings are optional.
82
 
 
83
 
        Returns a triple of package name the module is in, module name
84
 
        and filename of it, which can be a directory.
85
 
    """
86
 
 
87
 
    # We have many branches here, because there are a lot of cases to try.
88
 
    # pylint: disable=R0912
89
 
 
90
 
    if level > 1 and parent_package is not None:
91
 
        parent_package = '.'.join(parent_package.split('.')[:-level+1])
92
 
 
93
 
        if parent_package == "":
94
 
            parent_package = None
95
 
 
96
 
    if module_name != "" or parent_package is not None:
97
 
        try:
98
 
            module_filename, module_package_name = _findModule(
99
 
                module_name    = module_name,
100
 
                parent_package = parent_package
101
 
            )
102
 
        except ImportError:
103
 
            if warn and not _isWhiteListedNotExistingModule(module_name):
104
 
                key = module_name, parent_package, level
105
 
 
106
 
                if key not in warned_about:
107
 
                    warned_about.add(key)
108
 
 
109
 
                    if level == 0:
110
 
                        level_desc = "as absolute import"
111
 
                    elif level == -1:
112
 
                        level_desc = "as relative or absolute import"
113
 
                    elif level == 1:
114
 
                        level_desc = "%d package level up" % level
115
 
                    else:
116
 
                        level_desc = "%d package levels up" % level
117
 
 
118
 
                    if parent_package is not None:
119
 
                        warning(
120
 
                            "%s: Cannot find '%s' in package '%s' %s.",
121
 
                            source_ref.getAsString(),
122
 
                            module_name,
123
 
                            parent_package,
124
 
                            level_desc
125
 
                        )
126
 
                    else:
127
 
                        warning(
128
 
                            "%s: Cannot find '%s' %s.",
129
 
                            source_ref.getAsString(),
130
 
                            module_name,
131
 
                            level_desc
132
 
                        )
133
 
 
134
 
 
135
 
            if '.' in module_name:
136
 
                module_package_name = module_name[:module_name.rfind('.')]
137
 
            else:
138
 
                module_package_name = None
139
 
 
140
 
            module_filename = None
141
 
    else:
142
 
        if '.' in module_name:
143
 
            module_package_name = module_name[:module_name.rfind('.')]
144
 
        else:
145
 
            module_package_name = None
146
 
 
147
 
        module_filename = None
148
 
 
149
 
    if _debug_module_finding:
150
 
        print(
151
 
            "findModule: Result",
152
 
            module_package_name,
153
 
            module_name,
154
 
            module_filename
155
 
        )
156
 
 
157
 
    return module_package_name, module_name, module_filename
158
 
 
159
 
# Some platforms are case insensitive.
160
 
case_sensitive = not sys.platform.startswith(("win", "cygwin", "darwin"))
161
 
 
162
 
def _findModuleInPath2(module_name, search_path):
163
 
    """ This is out own module finding low level implementation.
164
 
 
165
 
        Just the full module name and search path are given. This is then
166
 
        tasked to raise ImportError or return a path if it finds it, or
167
 
        None, if it is a built-in.
168
 
    """
169
 
    # We have many branches here, because there are a lot of cases to try.
170
 
    # pylint: disable=R0912
171
 
 
172
 
    if imp.is_builtin(module_name):
173
 
        return None
174
 
 
175
 
    # We may have to decide between package and module, therefore build
176
 
    # a list of candidates.
177
 
    candidates = oset.OrderedSet()
178
 
 
179
 
    considered = set()
180
 
 
181
 
    for entry in search_path:
182
 
        # Don't try again, just with an entry of different casing or complete
183
 
        # duplicate.
184
 
        if Utils.normcase(entry) in considered:
185
 
            continue
186
 
        considered.add(Utils.normcase(entry))
187
 
 
188
 
        package_directory = os.path.join(entry, module_name)
189
 
 
190
 
        # First, check for a package with an init file, that would be the
191
 
        # first choice.
192
 
        if Utils.isDir(package_directory):
193
 
            for suffix in (".py", ".pyc"):
194
 
                package_file_name = "__init__" + suffix
195
 
 
196
 
                file_path = os.path.join(package_directory, package_file_name)
197
 
 
198
 
                if Utils.isFile(file_path):
199
 
                    candidates.add(
200
 
                        (entry, 1, package_directory)
201
 
                    )
202
 
                    break
203
 
            else:
204
 
                if Utils.python_version >= 330:
205
 
                    candidates.add(
206
 
                        (entry, 2, package_directory)
207
 
                    )
208
 
 
209
 
        # Then, check out suffixes of all kinds.
210
 
        for suffix, _mode, _type in imp.get_suffixes():
211
 
            file_path = Utils.joinpath(entry, module_name + suffix)
212
 
            if Utils.isFile(file_path):
213
 
                candidates.add(
214
 
                    (entry, 1, file_path)
215
 
                )
216
 
                break
217
 
 
218
 
    if candidates:
219
 
        # Ignore lower priority matches, package directories without init.
220
 
        min_prio = min(candidate[1] for candidate in candidates)
221
 
        candidates = [
222
 
            candidate
223
 
            for candidate in
224
 
            candidates
225
 
            if candidate[1] == min_prio
226
 
        ]
227
 
 
228
 
        # On case sensitive systems, no resolution needed.
229
 
        if case_sensitive:
230
 
            return candidates[0][2]
231
 
        else:
232
 
            if len(candidates) == 1:
233
 
                # Just one finding, good.
234
 
                return candidates[0][2]
235
 
            else:
236
 
                for candidate in candidates:
237
 
                    dir_listing = os.listdir(candidate[0])
238
 
 
239
 
                    for filename in dir_listing:
240
 
                        if Utils.joinpath(candidate[0], filename) == candidate[2]:
241
 
                            return candidate[2]
242
 
 
243
 
                # Please report this, but you may uncomment and have luck.
244
 
                assert False, candidates
245
 
 
246
 
                # If no case matches, just pick the first.
247
 
                return candidates[0][2]
248
 
 
249
 
    # Nothing found.
250
 
    raise ImportError
251
 
 
252
 
 
253
 
def _findModuleInPath(module_name, package_name):
254
 
    # We have many branches here, because there are a lot of cases to try.
255
 
    # pylint: disable=R0912
256
 
 
257
 
    if _debug_module_finding:
258
 
        print("_findModuleInPath: Enter", module_name, "in", package_name)
259
 
 
260
 
    assert main_path is not None
261
 
    extra_paths = [os.getcwd(), main_path]
262
 
 
263
 
    if package_name is not None:
264
 
        # Work around _findModuleInPath2 bug on at least Windows. Won't handle
265
 
        # module name empty in find_module. And thinking of it, how could it
266
 
        # anyway.
267
 
        if module_name == "":
268
 
            module_name = package_name.split('.')[-1]
269
 
            package_name = '.'.join(package_name.split('.')[:-1])
270
 
 
271
 
        def getPackageDirnames(element):
272
 
            yield Utils.joinpath(element,*package_name.split('.')), False
273
 
 
274
 
            if package_name == "win32com":
275
 
                yield Utils.joinpath(element,"win32comext"), True
276
 
 
277
 
        ext_path = []
278
 
        for element in extra_paths + sys.path:
279
 
            for package_dir, force_package in getPackageDirnames(element):
280
 
                if isPackageDir(package_dir) or force_package:
281
 
                    ext_path.append(package_dir)
282
 
 
283
 
        if _debug_module_finding:
284
 
            print("_findModuleInPath: Package, using extended path", ext_path)
285
 
 
286
 
        try:
287
 
            module_filename = _findModuleInPath2(
288
 
                module_name = module_name,
289
 
                search_path = ext_path
290
 
            )
291
 
 
292
 
            if _debug_module_finding:
293
 
                print(
294
 
                    "_findModuleInPath: _findModuleInPath2 worked",
295
 
                    module_filename,
296
 
                    package_name
297
 
                )
298
 
 
299
 
            return module_filename, package_name
300
 
        except ImportError:
301
 
            if _debug_module_finding:
302
 
                print("_findModuleInPath: _findModuleInPath2 failed to locate")
303
 
        except SyntaxError:
304
 
            # Warn user, as this is kind of unusual.
305
 
            warning(
306
 
                "%s: Module cannot be imported due to syntax errors.",
307
 
                module_name,
308
 
            )
309
 
 
310
 
            return None, None
311
 
 
312
 
    ext_path = extra_paths + sys.path
313
 
 
314
 
    if _debug_module_finding:
315
 
        print("_findModuleInPath: Non-package, using extended path", ext_path)
316
 
 
317
 
    try:
318
 
        module_filename = _findModuleInPath2(
319
 
            module_name = module_name,
320
 
            search_path = ext_path
321
 
        )
322
 
    except SyntaxError:
323
 
        # Warn user, as this is kind of unusual.
324
 
        warning(
325
 
            "%s: Module cannot be imported due to syntax errors.",
326
 
            module_name,
327
 
        )
328
 
 
329
 
        return None, None
330
 
 
331
 
    if _debug_module_finding:
332
 
        print("_findModuleInPath: _findModuleInPath2 gave", module_filename)
333
 
 
334
 
    return module_filename, None
335
 
 
336
 
 
337
 
def _findModule(module_name, parent_package):
338
 
    if _debug_module_finding:
339
 
        print("_findModule: Enter", module_name, "in", parent_package)
340
 
 
341
 
    # The os.path is strangely hacked into the "os" module, dispatching per
342
 
    # platform, we either cannot look into it, or we require that we resolve it
343
 
    # here correctly.
344
 
    if module_name == "os.path" and parent_package is None:
345
 
        parent_package = "os"
346
 
 
347
 
        module_name = Utils.basename(os.path.__file__)
348
 
        if module_name.endswith(".pyc"):
349
 
            module_name = module_name[:-4]
350
 
 
351
 
    assert module_name != "" or parent_package is not None
352
 
 
353
 
    # Built-in module names must not be searched any further.
354
 
    if module_name in sys.builtin_module_names and parent_package is None:
355
 
        return None, None
356
 
 
357
 
    if '.' in module_name:
358
 
        package_part = module_name[ : module_name.rfind('.') ]
359
 
        module_name = module_name[ module_name.rfind('.') + 1 : ]
360
 
 
361
 
        # Relative import
362
 
        if parent_package is not None:
363
 
            try:
364
 
                return _findModule(
365
 
                    module_name    = module_name,
366
 
                    parent_package = parent_package + '.' + package_part
367
 
                )
368
 
            except ImportError:
369
 
                pass
370
 
 
371
 
        # Absolute import
372
 
        return _findModule(
373
 
            module_name    = module_name,
374
 
            parent_package = package_part
375
 
        )
376
 
    else:
377
 
        module_filename, package = _findModuleInPath(
378
 
            module_name  = module_name,
379
 
            package_name = parent_package
380
 
        )
381
 
 
382
 
        if package == "":
383
 
            package = None
384
 
 
385
 
        return module_filename, package
386
 
 
387
 
def getModuleWhiteList():
388
 
    return (
389
 
        "mac", "nt", "os2", "posix", "_emx_link", "riscos", "ce", "riscospath",
390
 
        "riscosenviron", "Carbon.File", "org.python.core", "_sha", "_sha256",
391
 
        "array", "_sha512", "_md5", "_subprocess", "msvcrt", "cPickle",
392
 
        "marshal", "imp", "sys", "itertools", "cStringIO", "time", "zlib",
393
 
        "thread", "math", "errno", "operator", "signal", "gc", "exceptions",
394
 
        "win32process", "unicodedata", "__builtin__", "fcntl", "_socket",
395
 
        "_ssl", "pwd", "spwd", "_random", "grp", "_io", "_string", "select",
396
 
        "__main__", "_winreg", "_warnings", "_sre", "_functools", "_hashlib",
397
 
        "_collections", "_locale", "_codecs", "_weakref", "_struct",
398
 
        "_dummy_threading", "binascii", "datetime", "_ast", "xxsubtype",
399
 
        "_bytesio", "cmath", "_fileio", "aetypes", "aepack", "MacOS", "cd",
400
 
        "cl", "gdbm", "gl", "GL", "aetools", "_bisect", "_heapq", "_symtable",
401
 
        "syslog", "_datetime", "_elementtree", "_pickle", "_posixsubprocess",
402
 
        "_thread", "atexit", "pyexpat", "_imp", "_sha1", "faulthandler",
403
 
 
404
 
        # Python-Qt4 does these if missing python3 parts:
405
 
        "PyQt4.uic.port_v3.string_io", "PyQt4.uic.port_v3.load_plugin",
406
 
        "PyQt4.uic.port_v3.ascii_upper", "PyQt4.uic.port_v3.proxy_base",
407
 
        "PyQt4.uic.port_v3.as_string",
408
 
 
409
 
        # CPython3 does these:
410
 
        "builtins", "UserDict", "os.path", "StringIO",
411
 
 
412
 
        # test_applesingle.py
413
 
        "applesingle",
414
 
 
415
 
        # test_compile.py
416
 
        "__package__.module", "__mangled_mod",
417
 
 
418
 
        # test_dbm.py
419
 
        "dbm.dumb",
420
 
 
421
 
        # test_distutils.py
422
 
        "distutils.tests", "distutils.mwerkscompiler",
423
 
 
424
 
        # test_docxmlrpc.py
425
 
        "xmlrpc.server",
426
 
 
427
 
        # test_emails.py
428
 
        "email.test.test_email", "email.test.test_email_renamed",
429
 
        "email.test.test_email_codecs",
430
 
 
431
 
        # test/test_dbm_ndbm.py
432
 
        "dbm.ndbm",
433
 
 
434
 
        # test_frozen.py
435
 
        "__hello__", "__phello__", "__phello__.spam", "__phello__.foo",
436
 
 
437
 
        # test_imp.py
438
 
        "importlib.test.import_", "pep3147.foo", "pep3147",
439
 
 
440
 
        # test_import.py
441
 
        "RAnDoM", "infinite_reload", "test_trailing_slash", "nonexistent_xyzzy",
442
 
        "_parent_foo.bar", "_parent_foo", "test_unc_path",
443
 
 
444
 
        # test_importhooks.py
445
 
        "hooktestmodule", "hooktestpackage", "hooktestpackage.sub",
446
 
        "reloadmodule", "hooktestpackage.sub.subber", "hooktestpackage.oldabs",
447
 
        "hooktestpackage.newrel", "hooktestpackage.sub.subber.subest",
448
 
        "hooktestpackage.futrel", "sub", "hooktestpackage.newabs",
449
 
 
450
 
        # test_inspect.py
451
 
        "inspect_fodder3",
452
 
 
453
 
        # test_imageop.py
454
 
        "imgfile",
455
 
 
456
 
        # test_json.py
457
 
        "json.tests",
458
 
 
459
 
        # test_lib2to3.py
460
 
        "lib2to3.tests",
461
 
 
462
 
        # test_logging.py
463
 
        "win32evtlog", "win32evtlogutil",
464
 
 
465
 
        # test_macostools.py
466
 
        "macostools",
467
 
 
468
 
        # test_namespace_pkgs.py
469
 
        "foo.one", "foo.two", "parent.child.one", "parent.child.two",
470
 
        "parent.child.three", "bar.two", "a_test",
471
 
 
472
 
        # test_new.py
473
 
        "Spam",
474
 
        # est_ossaudiodev.py
475
 
        "ossaudiodev",
476
 
 
477
 
        # test_platform.py
478
 
        "gestalt",
479
 
 
480
 
        # test_pkg.py
481
 
        "t1", "t2", "t2.sub", "t2.sub.subsub", "t3.sub.subsub", "t5", "t6",
482
 
        "t7", "t7.sub", "t7.sub.subsub", "t8",
483
 
 
484
 
        # test_pkgutil.py
485
 
        "foo", "zipimport",
486
 
 
487
 
        # test_repr.py
488
 
        """areallylongpackageandmodulenametotestreprtruncation.\
489
 
areallylongpackageandmodulenametotestreprtruncation""",
490
 
 
491
 
        # test_robotparser.py
492
 
        "urllib.error",
493
 
 
494
 
        # test_runpy.py
495
 
        "test.script_helper",
496
 
 
497
 
        # test_strftime.py
498
 
        "java",
499
 
 
500
 
        # test_strop.py
501
 
        "strop",
502
 
 
503
 
        # test_sundry.py
504
 
        "distutils.emxccompiler", "os2emxpath",
505
 
 
506
 
        # test_tk.py
507
 
        "runtktests",
508
 
 
509
 
        # test_tools.py
510
 
        "analyze_dxp", "test_unparse",
511
 
 
512
 
        # test_traceback.py
513
 
        "test_bug737473",
514
 
 
515
 
        # test_xml_etree.py
516
 
        "xml.parsers.expat.errors",
517
 
 
518
 
        # test_zipimport_support.py
519
 
        "test_zipped_doctest", "zip_pkg",
520
 
 
521
 
        # test/test_zipimport_support.py
522
 
        "test.test_cmd_line_script",
523
 
 
524
 
        # Python3: modules that no longer exist
525
 
        "commands", "dummy_thread", "_dummy_thread", "httplib", "Queue", "sets",
526
 
 
527
 
        # Python2: modules that don't yet exit
528
 
        "http.client", "queue", "winreg",
529
 
 
530
 
        # Very old modules with older names
531
 
        "simplejson", "sets",
532
 
 
533
 
        # Standalone mode "site" import flexibilities
534
 
        "sitecustomize", "usercustomize", "apport_python_hook",
535
 
        "_frozen_importlib",
536
 
 
537
 
        # Standard library stuff that is optional
538
 
        "comtypes.server.inprocserver", "_tkinter", "_scproxy", "EasyDialogs",
539
 
        "SOCKS", "rourl2path", "_winapi", "win32api", "win32con", "_gestalt",
540
 
        "java.lang", "vms_lib", "ic", "readline", "termios", "_sysconfigdata",
541
 
        "al", "AL", "sunaudiodev", "SUNAUDIODEV", "Audio_mac", "nis",
542
 
        "test.test_MimeWriter", "dos", "win32pipe", "Carbon", "Carbon.Files",
543
 
        "sgi", "ctypes.macholib.dyld", "bsddb3", "_pybsddb", "_xmlrpclib",
544
 
        "netbios", "win32wnet", "email.Parser", "elementree.cElementTree",
545
 
        "elementree.ElementTree", "_gbdm", "resource", "crypt", "bz2", "dbm",
546
 
        "mmap",
547
 
 
548
 
        # Nuitka tests
549
 
        "test_common",
550
 
 
551
 
        # Mercurial test
552
 
        "statprof", "email.Generator", "email.Utils",
553
 
    )
554
 
 
555
 
def _isWhiteListedNotExistingModule(module_name):
556
 
    result = module_name in getModuleWhiteList()
557
 
 
558
 
    if not result and module_name in sys.builtin_module_names:
559
 
        warning("""\
560
 
Your CPython version has a built-in module '%s', that is not whitelisted
561
 
please report this to http://bugs.nuitka.net.""", module_name)
562
 
 
563
 
    return result
564
 
 
565
 
 
566
 
def getStandardLibraryPaths():
567
 
    """ Get the standard library paths.
568
 
 
569
 
    """
570
 
 
571
 
    # Using the function object to cache its result, avoiding global variable
572
 
    # usage.
573
 
    if not hasattr(getStandardLibraryPaths, "result"):
574
 
        os_filename = os.__file__
575
 
        if os_filename.endswith(".pyc"):
576
 
            os_filename = os_filename[:-1]
577
 
 
578
 
        os_path = Utils.normcase(Utils.dirname(os_filename))
579
 
 
580
 
        stdlib_paths = set([os_path])
581
 
 
582
 
        # Happens for virtualenv situation, some modules will come from the link
583
 
        # this points to.
584
 
        if Utils.isLink(os_filename):
585
 
            os_filename = Utils.readLink(os_filename)
586
 
            stdlib_paths.add(Utils.normcase(Utils.dirname(os_filename)))
587
 
 
588
 
        # Another possibility is "orig-prefix.txt" file near the os.py, which
589
 
        # points to the original install.
590
 
        orig_prefix_filename = Utils.joinpath(os_path, "orig-prefix.txt")
591
 
 
592
 
        if Utils.isFile(orig_prefix_filename):
593
 
            # Scan upwards, until we find a "bin" folder, with "activate" to
594
 
            # locate the structural path to be added. We do not know for sure
595
 
            # if there is a sub-directory under "lib" to use or not. So we try
596
 
            # to detect it.
597
 
            search = os_path
598
 
            lib_part = ""
599
 
 
600
 
            while os.path.splitdrive(search)[1] not in (os.path.sep, ""):
601
 
                if Utils.isFile(Utils.joinpath(search,"bin/activate")) or \
602
 
                   Utils.isFile(Utils.joinpath(search,"scripts/activate")):
603
 
                    break
604
 
 
605
 
                lib_part = Utils.joinpath(Utils.basename(search), lib_part)
606
 
 
607
 
                search = Utils.dirname(search)
608
 
 
609
 
            assert search and lib_part
610
 
 
611
 
            stdlib_paths.add(
612
 
                Utils.normcase(
613
 
                    Utils.joinpath(
614
 
                        open(orig_prefix_filename).read(),
615
 
                        lib_part,
616
 
                    )
617
 
                )
618
 
            )
619
 
 
620
 
        # And yet another possibility, for MacOS Homebrew created virtualenv
621
 
        # at least is a link ".Python", which points to the original install.
622
 
        python_link_filename = Utils.joinpath(os_path, "..", ".Python")
623
 
        if Utils.isLink(python_link_filename):
624
 
            stdlib_paths.add(
625
 
                Utils.normcase(
626
 
                    Utils.joinpath(
627
 
                        Utils.readLink(python_link_filename),
628
 
                        "lib"
629
 
                    )
630
 
                )
631
 
            )
632
 
 
633
 
        getStandardLibraryPaths.result = [
634
 
            Utils.normcase(stdlib_path)
635
 
            for stdlib_path in
636
 
            stdlib_paths
637
 
        ]
638
 
 
639
 
    return getStandardLibraryPaths.result
640
 
 
641
 
 
642
 
def isStandardLibraryPath(path):
643
 
    """ Check if a path is in the standard library.
644
 
 
645
 
    """
646
 
 
647
 
    path = Utils.normcase(path)
648
 
 
649
 
    # In virtualenv, the "site.py" lives in a place that suggests it is not in
650
 
    # standard library, although it is.
651
 
    if os.path.basename(path) == "site.py":
652
 
        return True
653
 
 
654
 
    # These never are in standard library paths.
655
 
    if "dist-packages" in path or "site-packages" in path:
656
 
        return False
657
 
 
658
 
    for candidate in getStandardLibraryPaths():
659
 
        if path.startswith(candidate):
660
 
            return True
661
 
    return False