Move salt.utils.istextfile to salt.utils.files.is_text_file

Moves the original function to `salt.utils.files.py`, adds a deprecation
warning to the original function, and updates all of the istextfile
references in salt code.
This commit is contained in:
rallytime 2017-08-29 16:51:56 -04:00
parent 3c454d4156
commit 1279556873
7 changed files with 75 additions and 58 deletions

View file

@ -126,8 +126,8 @@ def _binary_replace(old, new):
This function should only be run AFTER it has been determined that the
files differ.
'''
old_isbin = not salt.utils.istextfile(old)
new_isbin = not salt.utils.istextfile(new)
old_isbin = not salt.utils.files.is_text_file(old)
new_isbin = not salt.utils.files.is_text_file(new)
if any((old_isbin, new_isbin)):
if all((old_isbin, new_isbin)):
return u'Replace binary file'
@ -1436,7 +1436,7 @@ def comment_line(path,
raise SaltInvocationError('File not found: {0}'.format(path))
# Make sure it is a text file
if not salt.utils.istextfile(path):
if not salt.utils.files.is_text_file(path):
raise SaltInvocationError(
'Cannot perform string replacements on a binary file: {0}'.format(path))
@ -2180,7 +2180,7 @@ def replace(path,
else:
raise SaltInvocationError('File not found: {0}'.format(path))
if not salt.utils.istextfile(path):
if not salt.utils.files.is_text_file(path):
raise SaltInvocationError(
'Cannot perform string replacements on a binary file: {0}'
.format(path)
@ -2497,7 +2497,7 @@ def blockreplace(path,
'Only one of append and prepend_if_not_found is permitted'
)
if not salt.utils.istextfile(path):
if not salt.utils.files.is_text_file(path):
raise SaltInvocationError(
'Cannot perform string replacements on a binary file: {0}'
.format(path)

View file

@ -4366,7 +4366,7 @@ def comment(name, regex, char='#', backup='.bak'):
ret['result'] = __salt__['file.search'](name, unanchor_regex, multiline=True)
if slines != nlines:
if not salt.utils.istextfile(name):
if not salt.utils.files.is_text_file(name):
ret['changes']['diff'] = 'Replace binary file'
else:
# Changes happened, add them
@ -4478,7 +4478,7 @@ def uncomment(name, regex, char='#', backup='.bak'):
)
if slines != nlines:
if not salt.utils.istextfile(name):
if not salt.utils.files.is_text_file(name):
ret['changes']['diff'] = 'Replace binary file'
else:
# Changes happened, add them
@ -4721,7 +4721,7 @@ def append(name,
nlines = list(slines)
nlines.extend(append_lines)
if slines != nlines:
if not salt.utils.istextfile(name):
if not salt.utils.files.is_text_file(name):
ret['changes']['diff'] = 'Replace binary file'
else:
# Changes happened, add them
@ -4746,7 +4746,7 @@ def append(name,
nlines = nlines.splitlines()
if slines != nlines:
if not salt.utils.istextfile(name):
if not salt.utils.files.is_text_file(name):
ret['changes']['diff'] = 'Replace binary file'
else:
# Changes happened, add them
@ -4914,7 +4914,7 @@ def prepend(name,
if __opts__['test']:
nlines = test_lines + slines
if slines != nlines:
if not salt.utils.istextfile(name):
if not salt.utils.files.is_text_file(name):
ret['changes']['diff'] = 'Replace binary file'
else:
# Changes happened, add them
@ -4957,7 +4957,7 @@ def prepend(name,
nlines = nlines.splitlines(True)
if slines != nlines:
if not salt.utils.istextfile(name):
if not salt.utils.files.is_text_file(name):
ret['changes']['diff'] = 'Replace binary file'
else:
# Changes happened, add them

View file

@ -983,48 +983,6 @@ def arg_lookup(fun, aspec=None):
return ret
@jinja_filter('is_text_file')
def istextfile(fp_, blocksize=512):
'''
Uses heuristics to guess whether the given file is text or binary,
by reading a single block of bytes from the file.
If more than 30% of the chars in the block are non-text, or there
are NUL ('\x00') bytes in the block, assume this is a binary file.
'''
# Late import to avoid circular import.
import salt.utils.files
int2byte = (lambda x: bytes((x,))) if six.PY3 else chr
text_characters = (
b''.join(int2byte(i) for i in range(32, 127)) +
b'\n\r\t\f\b')
try:
block = fp_.read(blocksize)
except AttributeError:
# This wasn't an open filehandle, so treat it as a file path and try to
# open the file
try:
with salt.utils.files.fopen(fp_, 'rb') as fp2_:
block = fp2_.read(blocksize)
except IOError:
# Unable to open file, bail out and return false
return False
if b'\x00' in block:
# Files with null bytes are binary
return False
elif not block:
# An empty file is considered a valid text file
return True
try:
block.decode('utf-8')
return True
except UnicodeDecodeError:
pass
nontext = block.translate(None, text_characters)
return float(len(nontext)) / len(block) <= 0.30
@jinja_filter('sorted_ignorecase')
def isorted(to_sort):
'''
@ -3046,6 +3004,28 @@ def mkstemp(*args, **kwargs):
return salt.utils.files.mkstemp(*args, **kwargs)
@jinja_filter('is_text_file')
def istextfile(fp_, blocksize=512):
'''
Uses heuristics to guess whether the given file is text or binary,
by reading a single block of bytes from the file.
If more than 30% of the chars in the block are non-text, or there
are NUL ('\x00') bytes in the block, assume this is a binary file.
.. deprecated:: Oxygen
'''
# Late import to avoid circular import.
import salt.utils.files
salt.utils.versions.warn_until(
'Neon',
'Use of \'salt.utils.istextfile\' detected. This function has been moved '
'to \'salt.utils.files.is_text_file\' as of Salt Oxygen. This warning will '
'be removed in Salt Neon.'
)
return salt.utils.files.is_text_file(fp_, blocksize=blocksize)
def str_version_to_evr(verstring):
'''
Split the package version string into epoch, version and release.

View file

@ -498,3 +498,42 @@ def safe_filepath(file_path_name):
return os.sep.join([drive, path])
else:
return path
@jinja_filter('is_text_file')
def is_text_file(fp_, blocksize=512):
'''
Uses heuristics to guess whether the given file is text or binary,
by reading a single block of bytes from the file.
If more than 30% of the chars in the block are non-text, or there
are NUL ('\x00') bytes in the block, assume this is a binary file.
'''
int2byte = (lambda x: bytes((x,))) if six.PY3 else chr
text_characters = (
b''.join(int2byte(i) for i in range(32, 127)) +
b'\n\r\t\f\b')
try:
block = fp_.read(blocksize)
except AttributeError:
# This wasn't an open filehandle, so treat it as a file path and try to
# open the file
try:
with fopen(fp_, 'rb') as fp2_:
block = fp2_.read(blocksize)
except IOError:
# Unable to open file, bail out and return false
return False
if b'\x00' in block:
# Files with null bytes are binary
return False
elif not block:
# An empty file is considered a valid text file
return True
try:
block.decode('utf-8')
return True
except UnicodeDecodeError:
pass
nontext = block.translate(None, text_characters)
return float(len(nontext)) / len(block) <= 0.30

View file

@ -8,7 +8,6 @@ from __future__ import absolute_import
import os
# Import salt libs
import salt.utils
import salt.utils.files
# Import 3rd-party libs
@ -28,7 +27,7 @@ def find(path, saltenv='base'):
if os.path.isfile(full):
# Add it to the dict
with salt.utils.files.fopen(full, 'rb') as fp_:
if salt.utils.istextfile(fp_):
if salt.utils.files.is_text_file(fp_):
ret.append({full: 'txt'})
else:
ret.append({full: 'bin'})

View file

@ -9,7 +9,6 @@ from __future__ import absolute_import
import os
# Import salt libs
import salt.utils
import salt.utils.files
# Import 3rd-party libs
@ -29,7 +28,7 @@ def find(path, saltenv='base'):
if os.path.isfile(full):
# Add it to the dict
with salt.utils.files.fopen(full, 'rb') as fp_:
if salt.utils.istextfile(fp_):
if salt.utils.files.is_text_file(fp_):
ret.append({full: 'txt'})
else:
ret.append({full: 'bin'})

View file

@ -1181,7 +1181,7 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
ret.update({'name': name})
with patch.object(salt.utils.files, 'fopen',
MagicMock(mock_open(read_data=''))):
with patch.object(salt.utils, 'istextfile', mock_f):
with patch.object(salt.utils.files, 'is_text_file', mock_f):
with patch.dict(filestate.__opts__, {'test': True}):
change = {'diff': 'Replace binary file'}
comt = ('File {0} is set to be updated'