1# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
2# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
3
4"""Test file for run_python_file.
5
6This file is executed two ways::
7
8    $ coverage run try_execfile.py
9
10and::
11
12    $ python try_execfile.py
13
14The output is compared to see that the program execution context is the same
15under coverage and under Python.
16
17It is not crucial that the execution be identical, there are some differences
18that are OK.  This program canonicalizes the output to gloss over those
19differences and get a clean diff.
20
21"""
22
23import itertools
24import json
25import os
26import sys
27
28# sys.path varies by execution environments.  Coverage.py uses setuptools to
29# make console scripts, which means pkg_resources is imported.  pkg_resources
30# removes duplicate entries from sys.path.  So we do that too, since the extra
31# entries don't affect the running of the program.
32
33def same_file(p1, p2):
34    """Determine if `p1` and `p2` refer to the same existing file."""
35    if not p1:
36        return not p2
37    if not os.path.exists(p1):
38        return False
39    if not os.path.exists(p2):
40        return False
41    if hasattr(os.path, "samefile"):
42        return os.path.samefile(p1, p2)
43    else:
44        norm1 = os.path.normcase(os.path.normpath(p1))
45        norm2 = os.path.normcase(os.path.normpath(p2))
46        return norm1 == norm2
47
48def without_same_files(filenames):
49    """Return the list `filenames` with duplicates (by same_file) removed."""
50    reduced = []
51    for filename in filenames:
52        if not any(same_file(filename, other) for other in reduced):
53            reduced.append(filename)
54    return reduced
55
56cleaned_sys_path = [os.path.normcase(p) for p in without_same_files(sys.path)]
57
58DATA = "xyzzy"
59
60import __main__
61
62def my_function(a):
63    """A function to force execution of module-level values."""
64    return "my_fn(%r)" % a
65
66FN_VAL = my_function("fooey")
67
68loader = globals().get('__loader__')
69fullname = getattr(loader, 'fullname', None) or getattr(loader, 'name', None)
70
71# A more compact grouped-by-first-letter list of builtins.
72def word_group(w):
73    """Clump AB, CD, EF, etc."""
74    return chr((ord(w[0]) + 1) & 0xFE)
75
76builtin_dir = [" ".join(s) for _, s in itertools.groupby(dir(__builtins__), key=word_group)]
77
78globals_to_check = {
79    'os.getcwd': os.getcwd(),
80    '__name__': __name__,
81    '__file__': __file__,
82    '__doc__': __doc__,
83    '__builtins__.has_open': hasattr(__builtins__, 'open'),
84    '__builtins__.dir': builtin_dir,
85    '__loader__ exists': loader is not None,
86    '__loader__.fullname': fullname,
87    '__package__': __package__,
88    'DATA': DATA,
89    'FN_VAL': FN_VAL,
90    '__main__.DATA': getattr(__main__, "DATA", "nothing"),
91    'argv0': sys.argv[0],
92    'argv1-n': sys.argv[1:],
93    'path': cleaned_sys_path,
94}
95
96print(json.dumps(globals_to_check, indent=4, sort_keys=True))
97