]> crepu.dev Git - config.git/blob - djavu-asus/elpy/rpc-venv/lib/python3.11/site-packages/yapf/yapflib/file_resources.py
ActualizaciĆ³n de Readme
[config.git] / djavu-asus / elpy / rpc-venv / lib / python3.11 / site-packages / yapf / yapflib / file_resources.py
1 # Copyright 2015 Google Inc. All Rights Reserved.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 """Interface to file resources.
15
16 This module provides functions for interfacing with files: opening, writing, and
17 querying.
18 """
19
20 import codecs
21 import fnmatch
22 import os
23 import re
24 import sys
25 from configparser import ConfigParser
26 from tokenize import detect_encoding
27
28 from yapf.yapflib import errors
29 from yapf.yapflib import style
30
31 CR = '\r'
32 LF = '\n'
33 CRLF = '\r\n'
34
35
36 def _GetExcludePatternsFromYapfIgnore(filename):
37 """Get a list of file patterns to ignore from .yapfignore."""
38 ignore_patterns = []
39 if os.path.isfile(filename) and os.access(filename, os.R_OK):
40 with open(filename, 'r') as fd:
41 for line in fd:
42 if line.strip() and not line.startswith('#'):
43 ignore_patterns.append(line.strip())
44
45 if any(e.startswith('./') for e in ignore_patterns):
46 raise errors.YapfError('path in .yapfignore should not start with ./')
47
48 return ignore_patterns
49
50
51 def _GetExcludePatternsFromPyprojectToml(filename):
52 """Get a list of file patterns to ignore from pyproject.toml."""
53 ignore_patterns = []
54 try:
55 import tomli as tomllib
56 except ImportError:
57 raise errors.YapfError(
58 'tomli package is needed for using pyproject.toml as a '
59 'configuration file')
60
61 if os.path.isfile(filename) and os.access(filename, os.R_OK):
62 with open(filename, 'rb') as fd:
63 pyproject_toml = tomllib.load(fd)
64 ignore_patterns = pyproject_toml.get('tool',
65 {}).get('yapfignore',
66 {}).get('ignore_patterns', [])
67 if any(e.startswith('./') for e in ignore_patterns):
68 raise errors.YapfError('path in pyproject.toml should not start with ./')
69
70 return ignore_patterns
71
72
73 def GetExcludePatternsForDir(dirname):
74 """Return patterns of files to exclude from ignorefile in a given directory.
75
76 Looks for .yapfignore in the directory dirname.
77
78 Arguments:
79 dirname: (unicode) The name of the directory.
80
81 Returns:
82 A List of file patterns to exclude if ignore file is found, otherwise empty
83 List.
84 """
85 ignore_patterns = []
86
87 yapfignore_file = os.path.join(dirname, '.yapfignore')
88 if os.path.exists(yapfignore_file):
89 ignore_patterns += _GetExcludePatternsFromYapfIgnore(yapfignore_file)
90
91 pyproject_toml_file = os.path.join(dirname, 'pyproject.toml')
92 if os.path.exists(pyproject_toml_file):
93 ignore_patterns += _GetExcludePatternsFromPyprojectToml(pyproject_toml_file)
94 return ignore_patterns
95
96
97 def GetDefaultStyleForDir(dirname, default_style=style.DEFAULT_STYLE):
98 """Return default style name for a given directory.
99
100 Looks for .style.yapf or setup.cfg or pyproject.toml in the parent
101 directories.
102
103 Arguments:
104 dirname: (unicode) The name of the directory.
105 default_style: The style to return if nothing is found. Defaults to the
106 global default style ('pep8') unless otherwise specified.
107
108 Returns:
109 The filename if found, otherwise return the default style.
110 """
111 dirname = os.path.abspath(dirname)
112 while True:
113 # See if we have a .style.yapf file.
114 style_file = os.path.join(dirname, style.LOCAL_STYLE)
115 if os.path.exists(style_file):
116 return style_file
117
118 # See if we have a setup.cfg file with a '[yapf]' section.
119 config_file = os.path.join(dirname, style.SETUP_CONFIG)
120 try:
121 fd = open(config_file)
122 except IOError:
123 pass # It's okay if it's not there.
124 else:
125 with fd:
126 config = ConfigParser()
127 config.read_file(fd)
128 if config.has_section('yapf'):
129 return config_file
130
131 # See if we have a pyproject.toml file with a '[tool.yapf]' section.
132 config_file = os.path.join(dirname, style.PYPROJECT_TOML)
133 try:
134 fd = open(config_file, 'rb')
135 except IOError:
136 pass # It's okay if it's not there.
137 else:
138 with fd:
139 try:
140 import tomli as tomllib
141 except ImportError:
142 raise errors.YapfError(
143 'tomli package is needed for using pyproject.toml as a '
144 'configuration file')
145
146 pyproject_toml = tomllib.load(fd)
147 style_dict = pyproject_toml.get('tool', {}).get('yapf', None)
148 if style_dict is not None:
149 return config_file
150
151 if (not dirname or not os.path.basename(dirname) or
152 dirname == os.path.abspath(os.path.sep)):
153 break
154 dirname = os.path.dirname(dirname)
155
156 global_file = os.path.expanduser(style.GLOBAL_STYLE)
157 if os.path.exists(global_file):
158 return global_file
159
160 return default_style
161
162
163 def GetCommandLineFiles(command_line_file_list, recursive, exclude):
164 """Return the list of files specified on the command line."""
165 return _FindPythonFiles(command_line_file_list, recursive, exclude)
166
167
168 def WriteReformattedCode(filename,
169 reformatted_code,
170 encoding='',
171 in_place=False):
172 """Emit the reformatted code.
173
174 Write the reformatted code into the file, if in_place is True. Otherwise,
175 write to stdout.
176
177 Arguments:
178 filename: (unicode) The name of the unformatted file.
179 reformatted_code: (unicode) The reformatted code.
180 encoding: (unicode) The encoding of the file.
181 in_place: (bool) If True, then write the reformatted code to the file.
182 """
183 if in_place:
184 with codecs.open(filename, mode='w', encoding=encoding) as fd:
185 fd.write(reformatted_code)
186 else:
187 sys.stdout.buffer.write(reformatted_code.encode('utf-8'))
188
189
190 def LineEnding(lines):
191 """Retrieve the line ending of the original source."""
192 endings = {CRLF: 0, CR: 0, LF: 0}
193 for line in lines:
194 if line.endswith(CRLF):
195 endings[CRLF] += 1
196 elif line.endswith(CR):
197 endings[CR] += 1
198 elif line.endswith(LF):
199 endings[LF] += 1
200 return sorted((LF, CRLF, CR), key=endings.get, reverse=True)[0]
201
202
203 def _FindPythonFiles(filenames, recursive, exclude):
204 """Find all Python files."""
205 if exclude and any(e.startswith('./') for e in exclude):
206 raise errors.YapfError("path in '--exclude' should not start with ./")
207 exclude = exclude and [e.rstrip('/' + os.path.sep) for e in exclude]
208
209 python_files = []
210 for filename in filenames:
211 if filename != '.' and exclude and IsIgnored(filename, exclude):
212 continue
213 if os.path.isdir(filename):
214 if not recursive:
215 raise errors.YapfError(
216 "directory specified without '--recursive' flag: %s" % filename)
217
218 # TODO(morbo): Look into a version of os.walk that can handle recursion.
219 excluded_dirs = []
220 for dirpath, dirnames, filelist in os.walk(filename):
221 if dirpath != '.' and exclude and IsIgnored(dirpath, exclude):
222 excluded_dirs.append(dirpath)
223 continue
224 elif any(dirpath.startswith(e) for e in excluded_dirs):
225 continue
226 for f in filelist:
227 filepath = os.path.join(dirpath, f)
228 if exclude and IsIgnored(filepath, exclude):
229 continue
230 if IsPythonFile(filepath):
231 python_files.append(filepath)
232 # To prevent it from scanning the contents excluded folders, os.walk()
233 # lets you amend its list of child dirs `dirnames`. These edits must be
234 # made in-place instead of creating a modified copy of `dirnames`.
235 # list.remove() is slow and list.pop() is a headache. Instead clear
236 # `dirnames` then repopulate it.
237 dirnames_ = [dirnames.pop(0) for i in range(len(dirnames))]
238 for dirname in dirnames_:
239 dir_ = os.path.join(dirpath, dirname)
240 if IsIgnored(dir_, exclude):
241 excluded_dirs.append(dir_)
242 else:
243 dirnames.append(dirname)
244
245 elif os.path.isfile(filename):
246 python_files.append(filename)
247
248 return python_files
249
250
251 def IsIgnored(path, exclude):
252 """Return True if filename matches any patterns in exclude."""
253 if exclude is None:
254 return False
255 path = path.lstrip(os.path.sep)
256 while path.startswith('.' + os.path.sep):
257 path = path[2:]
258 return any(fnmatch.fnmatch(path, e.rstrip(os.path.sep)) for e in exclude)
259
260
261 def IsPythonFile(filename):
262 """Return True if filename is a Python file."""
263 if os.path.splitext(filename)[1] == '.py':
264 return True
265
266 try:
267 with open(filename, 'rb') as fd:
268 encoding = detect_encoding(fd.readline)[0]
269
270 # Check for correctness of encoding.
271 with codecs.open(filename, mode='r', encoding=encoding) as fd:
272 fd.read()
273 except UnicodeDecodeError:
274 encoding = 'latin-1'
275 except (IOError, SyntaxError):
276 # If we fail to detect encoding (or the encoding cookie is incorrect - which
277 # will make detect_encoding raise SyntaxError), assume it's not a Python
278 # file.
279 return False
280
281 try:
282 with codecs.open(filename, mode='r', encoding=encoding) as fd:
283 first_line = fd.readline(256)
284 except IOError:
285 return False
286
287 return re.match(r'^#!.*\bpython[23]?\b', first_line)
288
289
290 def FileEncoding(filename):
291 """Return the file's encoding."""
292 with open(filename, 'rb') as fd:
293 return detect_encoding(fd.readline)[0]