Cookiecutter: Symlinks become normal directories when cookiecutter is run

Created on 7 Dec 2016  ยท  3Comments  ยท  Source: cookiecutter/cookiecutter

  • Cookiecutter version: master
  • Template project url:
  • Python version: 3.5
  • Operating System: OSX

Description:

Symlinks within cookiecutter templates are copied as directories rather than as symlinks.

What I've run:

Here's a cookiecutter template with just the name variable in cookiecutter.json:

12:42 $ tree symlink-cc/
symlink-cc/
โ”œโ”€โ”€ cookiecutter.json
โ””โ”€โ”€ {{\ cookiecutter.name\ }}
    โ”œโ”€โ”€ original
    โ”‚ย ย  โ””โ”€โ”€ afile.txt
    โ””โ”€โ”€ symlink -> original

I can successfully run cookiecutter.

12:38 $ cookiecutter symlink-cc
name [pjb]: pjb

In the output, the symlink turns into a standard directory.

12:42 $ tree pjb/
pjb/
โ”œโ”€โ”€ original
โ”‚ย ย  โ””โ”€โ”€ afile.txt
โ””โ”€โ”€ symlink

Working on a PR that should fix this.

Most helpful comment

Nice catch @pjbull! We look forward to your PR ๐Ÿ˜ƒ

All 3 comments

Nice catch @pjbull! We look forward to your PR ๐Ÿ˜ƒ

What I would like is support for copying all symlinks as-is, not just symlinks to existing directories.

My use case is I have symlinks in the cooiecutter template that use relative paths to point to directories that do not exist (specifically, to directories that will be created by npm install in a new project).

In that situation, symlinks are added to os.walk's files list (not dirs list), and cookiecutter stops with an IOError exception when going to read the symlink's target.

Traceback (most recent call last):
  File "/Users/david/venv/bin/cookiecutter", line 11, in <module>
    load_entry_point('cookiecutter==1.5.1', 'console_scripts', 'cookiecutter')()
  File "/Users/david/venv/lib/python2.7/site-packages/click/core.py", line 722, in __call__
    return self.main(*args, **kwargs)
  File "/Users/david/venv/lib/python2.7/site-packages/click/core.py", line 697, in main
    rv = self.invoke(ctx)
  File "/Users/david/venv/lib/python2.7/site-packages/click/core.py", line 895, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/Users/david/venv/lib/python2.7/site-packages/click/core.py", line 535, in invoke
    return callback(*args, **kwargs)
  File "/Users/david/venv/lib/python2.7/site-packages/cookiecutter/cli.py", line 123, in main
    default_config=default_config,
  File "/Users/david/venv/lib/python2.7/site-packages/cookiecutter/main.py", line 91, in cookiecutter
    output_dir=output_dir
  File "/Users/david/venv/lib/python2.7/site-packages/cookiecutter/generate.py", line 349, in generate_files
    generate_file(project_dir, infile, context, env)
  File "/Users/david/venv/lib/python2.7/site-packages/cookiecutter/generate.py", line 153, in generate_file
    if is_binary(infile):
  File "/Users/david/venv/lib/python2.7/site-packages/binaryornot/check.py", line 24, in is_binary
    chunk = get_starting_chunk(filename)
  File "/Users/david/venv/lib/python2.7/site-packages/binaryornot/helpers.py", line 32, in get_starting_chunk
    with open(filename, 'rb') as f:
IOError: [Errno 2] No such file or directory: u'{{ cookiecutter.static_dir }}/src/bulma'

This is Python 2.7, current master of cookiecutter (a000a8f236a79b7d2dbffc6e945fcd69007a9baf).

According to my analysis the culprit of this behavior is shutil.copytree. In Python 3.3 this function also causes the following error (for a symlink framework to a directory):

Traceback (most recent call last):
  File "/home/peter/.virtualenvs/name-of-the-project/bin/cookiecutter", line 11, in <module>
    sys.exit(main())
  File "/home/peter/.virtualenvs/name-of-the-project/lib/python3.3/site-packages/click/core.py", line 722, in __call__
    return self.main(*args, **kwargs)
  File "/home/peter/.virtualenvs/name-of-the-project/lib/python3.3/site-packages/click/core.py", line 697, in main
    rv = self.invoke(ctx)
  File "/home/peter/.virtualenvs/name-of-the-project/lib/python3.3/site-packages/click/core.py", line 895, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/peter/.virtualenvs/name-of-the-project/lib/python3.3/site-packages/click/core.py", line 535, in invoke
    return callback(*args, **kwargs)
  File "/home/peter/.virtualenvs/name-of-the-project/lib/python3.3/site-packages/cookiecutter/cli.py", line 123, in main
    default_config=default_config,
  File "/home/peter/.virtualenvs/name-of-the-project/lib/python3.3/site-packages/cookiecutter/main.py", line 91, in cookiecutter
    output_dir=output_dir
  File "/home/peter/.virtualenvs/name-of-the-project/lib/python3.3/site-packages/cookiecutter/generate.py", line 314, in generate_files
    shutil.copytree(indir, outdir)
  File "/home/peter/.virtualenvs/name-of-the-project/lib/python3.3/shutil.py", line 343, in copytree
    raise Error(errors)
shutil.Error: [('_/frameworks/Symfony/web/bundles/framework', '/home/peter/Development/repos/name-of-the-project/_/frameworks/Symfony/web/bundles/framework', "[Errno 21] Is a directory: '_/frameworks/Symfony/web/bundles/framework'")]

There is a bug report on the Python bug tracker for this issue: https://bugs.python.org/issue21697

According to https://github.com/pypa/pip/issues/1006#issuecomment-19890662 the error should go away if we set symlinks=True in shutil.copytree().

Details on the cookiecutter and Python version I used for the above stacktrace:

$ cookiecutter --version
Cookiecutter 1.5.1 from /home/peter/.virtualenvs/name-of-the-project/lib/python3.3/site-packages (Python 3.3)
Was this page helpful?
0 / 5 - 0 ratings