11 import re |
11 import re |
12 import sys |
12 import sys |
13 |
13 |
14 from coverage import env |
14 from coverage import env |
15 from coverage.backward import unicode_class |
15 from coverage.backward import unicode_class |
16 from coverage.misc import CoverageException, join_regex |
16 from coverage.misc import contract, CoverageException, join_regex, isolate_module |
17 |
17 |
18 |
18 |
19 RELATIVE_DIR = None |
19 os = isolate_module(os) |
20 CANONICAL_FILENAME_CACHE = {} |
|
21 |
20 |
22 |
21 |
23 def set_relative_directory(): |
22 def set_relative_directory(): |
24 """Set the directory that `relative_filename` will be relative to.""" |
23 """Set the directory that `relative_filename` will be relative to.""" |
25 global RELATIVE_DIR, CANONICAL_FILENAME_CACHE |
24 global RELATIVE_DIR, CANONICAL_FILENAME_CACHE |
29 |
28 |
30 # Cache of results of calling the canonical_filename() method, to |
29 # Cache of results of calling the canonical_filename() method, to |
31 # avoid duplicating work. |
30 # avoid duplicating work. |
32 CANONICAL_FILENAME_CACHE = {} |
31 CANONICAL_FILENAME_CACHE = {} |
33 |
32 |
|
33 |
34 def relative_directory(): |
34 def relative_directory(): |
35 """Return the directory that `relative_filename` is relative to.""" |
35 """Return the directory that `relative_filename` is relative to.""" |
36 return RELATIVE_DIR |
36 return RELATIVE_DIR |
37 |
37 |
|
38 |
|
39 @contract(returns='unicode') |
38 def relative_filename(filename): |
40 def relative_filename(filename): |
39 """Return the relative form of `filename`. |
41 """Return the relative form of `filename`. |
40 |
42 |
41 The file name will be relative to the current directory when the |
43 The file name will be relative to the current directory when the |
42 `set_relative_directory` was called. |
44 `set_relative_directory` was called. |
43 |
45 |
44 """ |
46 """ |
45 fnorm = os.path.normcase(filename) |
47 fnorm = os.path.normcase(filename) |
46 if fnorm.startswith(RELATIVE_DIR): |
48 if fnorm.startswith(RELATIVE_DIR): |
47 filename = filename[len(RELATIVE_DIR):] |
49 filename = filename[len(RELATIVE_DIR):] |
48 return filename |
50 return unicode_filename(filename) |
49 |
51 |
|
52 |
|
53 @contract(returns='unicode') |
50 def canonical_filename(filename): |
54 def canonical_filename(filename): |
51 """Return a canonical file name for `filename`. |
55 """Return a canonical file name for `filename`. |
52 |
56 |
53 An absolute path with no redundant components and normalized case. |
57 An absolute path with no redundant components and normalized case. |
54 |
58 |
56 if filename not in CANONICAL_FILENAME_CACHE: |
60 if filename not in CANONICAL_FILENAME_CACHE: |
57 if not os.path.isabs(filename): |
61 if not os.path.isabs(filename): |
58 for path in [os.curdir] + sys.path: |
62 for path in [os.curdir] + sys.path: |
59 if path is None: |
63 if path is None: |
60 continue |
64 continue |
61 f = path + os.sep + filename |
65 f = os.path.join(path, filename) |
62 if os.path.exists(f): |
66 if os.path.exists(f): |
63 filename = f |
67 filename = f |
64 break |
68 break |
65 cf = abs_file(filename) |
69 cf = abs_file(filename) |
66 CANONICAL_FILENAME_CACHE[filename] = cf |
70 CANONICAL_FILENAME_CACHE[filename] = cf |
112 normtail = os.path.normcase(tail) |
116 normtail = os.path.normcase(tail) |
113 for f in files: |
117 for f in files: |
114 if os.path.normcase(f) == normtail: |
118 if os.path.normcase(f) == normtail: |
115 tail = f |
119 tail = f |
116 break |
120 break |
117 actpath = head.strip(os.sep) + os.sep + tail |
121 actpath = os.path.join(head, tail) |
118 _ACTUAL_PATH_CACHE[path] = actpath |
122 _ACTUAL_PATH_CACHE[path] = actpath |
119 return actpath |
123 return actpath |
120 |
124 |
121 else: |
125 else: |
122 def actual_path(filename): |
126 def actual_path(filename): |
123 """The actual path for non-Windows platforms.""" |
127 """The actual path for non-Windows platforms.""" |
124 return filename |
128 return filename |
125 |
129 |
126 |
130 |
|
131 if env.PY2: |
|
132 @contract(returns='unicode') |
|
133 def unicode_filename(filename): |
|
134 """Return a Unicode version of `filename`.""" |
|
135 if isinstance(filename, str): |
|
136 encoding = sys.getfilesystemencoding() or sys.getdefaultencoding() |
|
137 filename = filename.decode(encoding, "replace") |
|
138 return filename |
|
139 else: |
|
140 @contract(filename='unicode', returns='unicode') |
|
141 def unicode_filename(filename): |
|
142 """Return a Unicode version of `filename`.""" |
|
143 return filename |
|
144 |
|
145 |
|
146 @contract(returns='unicode') |
127 def abs_file(filename): |
147 def abs_file(filename): |
128 """Return the absolute normalized form of `filename`.""" |
148 """Return the absolute normalized form of `filename`.""" |
129 path = os.path.expandvars(os.path.expanduser(filename)) |
149 path = os.path.expandvars(os.path.expanduser(filename)) |
130 path = os.path.abspath(os.path.realpath(path)) |
150 path = os.path.abspath(os.path.realpath(path)) |
131 path = actual_path(path) |
151 path = actual_path(path) |
|
152 path = unicode_filename(path) |
132 return path |
153 return path |
|
154 |
|
155 |
|
156 RELATIVE_DIR = None |
|
157 CANONICAL_FILENAME_CACHE = None |
|
158 set_relative_directory() |
133 |
159 |
134 |
160 |
135 def isabs_anywhere(filename): |
161 def isabs_anywhere(filename): |
136 """Is `filename` an absolute path on any OS?""" |
162 """Is `filename` an absolute path on any OS?""" |
137 return ntpath.isabs(filename) or posixpath.isabs(filename) |
163 return ntpath.isabs(filename) or posixpath.isabs(filename) |
347 for filename in filenames: |
373 for filename in filenames: |
348 # We're only interested in files that look like reasonable Python |
374 # We're only interested in files that look like reasonable Python |
349 # files: Must end with .py or .pyw, and must not have certain funny |
375 # files: Must end with .py or .pyw, and must not have certain funny |
350 # characters that probably mean they are editor junk. |
376 # characters that probably mean they are editor junk. |
351 if re.match(r"^[^.#~!$@%^&*()+=,]+\.pyw?$", filename): |
377 if re.match(r"^[^.#~!$@%^&*()+=,]+\.pyw?$", filename): |
352 yield dirpath + os.sep + filename |
378 yield os.path.join(dirpath, filename) |
353 |
379 |
354 # |
380 # |
355 # eflag: FileType = Python2 |
381 # eflag: FileType = Python2 |