Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added: End of line detection and configuration. #1407

Merged
merged 18 commits into from May 28, 2020
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -44,6 +44,7 @@ nosetests.xml
coverage.xml
*,cover
.hypothesis/
.pytest_cache

# Translations
*.mo
Expand Down
8 changes: 7 additions & 1 deletion cookiecutter/generate.py
Expand Up @@ -170,9 +170,15 @@ def generate_file(project_dir, infile, context, env, skip_if_file_exists=False):
raise
rendered_file = tmpl.render(**context)

# Detect original file newline to output the rendered file
# note: newline='' ensures newlines are not converted
with io.open(infile, 'r', encoding='utf-8', newline='') as rd:
rd.readline() # Read the first line to load 'newlines' value
newline = getattr(rd, 'newlines')

logger.debug('Writing contents to file %s', outfile)

with io.open(outfile, 'w', encoding='utf-8') as fh:
with io.open(outfile, 'w', encoding='utf-8', newline=newline) as fh:
insspb marked this conversation as resolved.
Show resolved Hide resolved
fh.write(rendered_file)

# Apply file permissions to output file
Expand Down
5 changes: 5 additions & 0 deletions tests/files/{{generate_file}}_crlf_newlines.txt
@@ -0,0 +1,5 @@
newline is CRLF
newline is CRLF
newline is CRLF


3 changes: 3 additions & 0 deletions tests/files/{{generate_file}}_lf_newlines.txt
@@ -0,0 +1,3 @@
newline is LF
newline is LF

9 changes: 9 additions & 0 deletions tests/test-generate-files-line-end/cookiecutter.json
@@ -0,0 +1,9 @@
{
"full_name": "Philippe Ombredanne",
"year": "2015",
"color": "blue",
"letter": "D",
"folder_name": "im_a.dir",
"filename": "im_a.file",
"test_name": "output_folder"
}
@@ -0,0 +1 @@
The color is {{ cookiecutter.color }} and the letter is {{ cookiecutter.letter }}.
@@ -0,0 +1,3 @@
Hi!
My name is {{ cookiecutter.full_name }}.
It is {{ cookiecutter.year }}.
@@ -0,0 +1 @@
print("This is the contents of {{ cookiecutter.filename }}.py.")
@@ -0,0 +1 @@
I eat {{ cookiecutter.food }}
35 changes: 35 additions & 0 deletions tests/test_generate_file.py
Expand Up @@ -20,6 +20,10 @@ def tear_down():
yield
if os.path.exists('tests/files/cheese.txt'):
os.remove('tests/files/cheese.txt')
if os.path.exists('tests/files/cheese_lf_newlines.txt'):
os.remove('tests/files/cheese_lf_newlines.txt')
if os.path.exists('tests/files/cheese_crlf_newlines.txt'):
os.remove('tests/files/cheese_crlf_newlines.txt')


@pytest.fixture
Expand Down Expand Up @@ -117,3 +121,34 @@ def test_generate_file_verbose_template_syntax_error(env, expected_msg):
env=env,
)
assert str(exception.value) == expected_msg


def test_generate_file_does_not_translate_lf_newlines_to_crlf(env):
"""Verify that file generation use same line ending, as in source file."""
infile = 'tests/files/{{generate_file}}_lf_newlines.txt'
generate.generate_file(
project_dir=".", infile=infile, context={'generate_file': 'cheese'}, env=env
)
# this generated file should not have a CRLF line ending
gf = 'tests/files/cheese_lf_newlines.txt'
for line in open(gf):
msg = 'Missing LF line ending for generated file: {gf}'
assert line.endswith('\n'), msg.format(**locals())
msg = 'Incorrect CRLF line ending for generated file: {gf}'
assert not line.endswith('\r\n'), msg.format(**locals())


def test_generate_file_does_translate_crlf_newlines_to_lf(env):
"""Verify that file generation use same line ending, as in source file."""
infile = 'tests/files/{{generate_file}}_crlf_newlines.txt'
generate.generate_file(
project_dir=".", infile=infile, context={'generate_file': 'cheese'}, env=env
)

# this generated file should not have a CRLF line ending
gf = 'tests/files/cheese_crlf_newlines.txt'
for line in open(gf):
msg = 'Missing LF line ending for generated file: {gf}'
assert line.endswith('\n'), msg.format(**locals())
msg = 'Incorrect CRLF line ending for generated file: {gf}'
assert not line.endswith('\r\n'), msg.format(**locals())
49 changes: 49 additions & 0 deletions tests/test_generate_files.py
Expand Up @@ -39,6 +39,8 @@ def remove_additional_folders():
utils.rmtree('tests/custom_output_dir')
if os.path.exists('inputpermissions'):
utils.rmtree('inputpermissions')
if os.path.exists('tests/line-end-output'):
utils.rmtree('tests/line-end-output')


@pytest.mark.usefixtures('clean_system', 'remove_additional_folders')
Expand Down Expand Up @@ -84,6 +86,25 @@ def test_generate_files_with_trailing_newline():
with io.open(newline_file, 'r', encoding='utf-8') as f:
simple_text = f.read()
assert simple_text == 'I eat pizzä\n'
assert f.newlines == '\n'


@pytest.mark.usefixtures('clean_system', 'remove_additional_folders')
def test_generate_files_with_windows_newline():
generate.generate_files(
context={
'cookiecutter': {'food': 'pizzä'}
},
repo_dir='tests/test-generate-files'
)

newline_file = 'inputpizzä/simple-with-newline-crlf.txt'
assert os.path.isfile(newline_file)

with io.open(newline_file, 'r') as f:
f.read()

assert f.newlines == '\r\n'


@pytest.mark.usefixtures('clean_system', 'remove_additional_folders')
Expand Down Expand Up @@ -171,6 +192,34 @@ def test_generate_files_permissions():
assert tests_script_file_mode == input_script_file_mode


@pytest.mark.usefixtures('clean_system', 'remove_additional_folders')
def test_generate_files_does_not_change_line_endings():
"""Verify that template generation use same line ending, as in source file."""
context = generate.generate_context(
context_file='tests/test-generate-files-line-end/cookiecutter.json'
)

generate.generate_files(
context=context,
repo_dir='tests/test-generate-files-line-end',
output_dir='tests/line-end-output',
)

generated_files = [
'tests/line-end-output/output_folder/something.txt',
'tests/line-end-output/output_folder/folder/in_folder.txt',
'tests/line-end-output/output_folder/im_a.dir/im_a.file.py',
]

# these generated files should not have a CRLF line ending
for gf in generated_files:
for line in open(gf):
msg = 'Missing LF line ending for generated file: {gf}'
assert line.endswith('\n'), msg.format(**locals())
msg = 'Incorrect CRLF line ending for generated file: {gf}'
assert not line.endswith('\r\n'), msg.format(**locals())


@pytest.mark.usefixtures('clean_system', 'remove_additional_folders')
def test_generate_files_with_overwrite_if_exists_with_skip_if_file_exists():
"""Verify `skip_if_file_exist` has priority over `overwrite_if_exists`."""
Expand Down