]> crepu.dev Git - config.git/blame - djavu-asus/elpy/rpc-venv/lib/python3.11/site-packages/yapf/yapflib/file_resources.py
Actualizado el Readme
[config.git] / djavu-asus / elpy / rpc-venv / lib / python3.11 / site-packages / yapf / yapflib / file_resources.py
CommitLineData
53e6db90
DC
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
16This module provides functions for interfacing with files: opening, writing, and
17querying.
18"""
19
20import codecs
21import fnmatch
22import os
23import re
24import sys
25from configparser import ConfigParser
26from tokenize import detect_encoding
27
28from yapf.yapflib import errors
29from yapf.yapflib import style
30
31CR = '\r'
32LF = '\n'
33CRLF = '\r\n'
34
35
36def _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
51def _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
73def 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
97def 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
163def 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
168def 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
190def 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
203def _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
251def 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
261def 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
290def FileEncoding(filename):
291 """Return the file's encoding."""
292 with open(filename, 'rb') as fd:
293 return detect_encoding(fd.readline)[0]