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

Permission denied error when sphinx template files are read-only #54

Open
Apteryks opened this issue Apr 14, 2022 · 1 comment · May be fixed by #56
Open

Permission denied error when sphinx template files are read-only #54

Apteryks opened this issue Apr 14, 2022 · 1 comment · May be fixed by #56
Labels
Milestone

Comments

@Apteryks
Copy link

Hi,

I found this issue via the test suite, and traced it to the fact that the source files copied by sphinx itself when instantiating the Sphinx object (sphinx_app in sphinxify.py) were read-only. The error looks like:

___________________________ test_sphinxify[outline] ____________________________

build_oinfo = <function fixture_build_oinfo.<locals>._build_oinfo at 0x7fffd551a1f0>
set_docrepr_options = <function fixture_set_docrepr_options.<locals>._set_docrepr_options at 0x7fffd551ae50>
open_browser = <function open_browser.<locals>._open_browser at 0x7fffd54de310>
compare_screenshots = <function compare_screenshots.<locals>._compare_screenshots at 0x7fffd54de4c0>
test_id = 'outline', obj = <ufunc 'sin'>, oinfo_data = {'name': 'sin'}
docrepr_options = {'outline': True}

    @pytest.mark.asyncio
    @pytest.mark.parametrize(
        ('test_id', 'obj', 'oinfo_data', 'docrepr_options'),
        _test_cases_to_params(TEST_CASES),
        ids=list(TEST_CASES.keys()),
        )
    async def test_sphinxify(
            build_oinfo, set_docrepr_options, open_browser, compare_screenshots,
            test_id, obj, oinfo_data, docrepr_options,
            ):
        """Test the operation of the Sphinxify module on various docstrings."""
        if (oinfo_data.get('docstring', None) == PLOT_DOCSTRING
                and sys.version_info.major == 3
                and sys.version_info.minor == 6
                and sys.platform.startswith('win')):
            pytest.skip(
                'Plot fails on Py3.6 on Windows; older version of Matplotlib?')
    
        oinfo = build_oinfo(obj, **oinfo_data)
        set_docrepr_options(**docrepr_options)
    
>       url = docrepr.sphinxify.rich_repr(oinfo)

docrepr/tests/test_output.py:240: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/gnu/store/dmcslfpdfyv74mfwwbqliksr255xk8iw-python-docrepr-0.2.0/lib/python3.9/site-packages/docrepr/sphinxify.py:450: in rich_repr
    class_doc = sphinxify(wrapped_class_docstring, srcdir)
/gnu/store/dmcslfpdfyv74mfwwbqliksr255xk8iw-python-docrepr-0.2.0/lib/python3.9/site-packages/docrepr/sphinxify.py:405: in sphinxify
    merge_directories(destdir, srcdir)
/gnu/store/dmcslfpdfyv74mfwwbqliksr255xk8iw-python-docrepr-0.2.0/lib/python3.9/site-packages/docrepr/utils.py:52: in merge_directories
    shutil.copytree(source, destination, dirs_exist_ok=True)
/gnu/store/b6j1qw1a5rkbfvcy7lc9fm95abbzpa4x-python-3.9.9/lib/python3.9/shutil.py:566: in copytree
    return _copytree(entries=entries, src=src, dst=dst, symlinks=symlinks,
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

entries = [<DirEntry 'docstring.html'>, <DirEntry 'search.html'>, <DirEntry '_static'>, <DirEntry '.buildinfo'>, <DirEntry 'searchindex.js'>, <DirEntry 'objects.inv'>]
src = '/tmp/guix-build-python-docrepr-0.2.0.drv-0/tmpev83mper'
dst = '/tmp/guix-build-python-docrepr-0.2.0.drv-0/docrepr-None/tmpbm5c0r1l'
symlinks = False, ignore = None
copy_function = <function copy2 at 0x7ffff73c5d30>
ignore_dangling_symlinks = False, dirs_exist_ok = True

    def _copytree(entries, src, dst, symlinks, ignore, copy_function,
                  ignore_dangling_symlinks, dirs_exist_ok=False):
        if ignore is not None:
            ignored_names = ignore(os.fspath(src), [x.name for x in entries])
        else:
            ignored_names = set()
    
        os.makedirs(dst, exist_ok=dirs_exist_ok)
        errors = []
        use_srcentry = copy_function is copy2 or copy_function is copy
    
        for srcentry in entries:
            if srcentry.name in ignored_names:
                continue
            srcname = os.path.join(src, srcentry.name)
            dstname = os.path.join(dst, srcentry.name)
            srcobj = srcentry if use_srcentry else srcname
            try:
                is_symlink = srcentry.is_symlink()
                if is_symlink and os.name == 'nt':
                    # Special check for directory junctions, which appear as
                    # symlinks but we want to recurse.
                    lstat = srcentry.stat(follow_symlinks=False)
                    if lstat.st_reparse_tag == stat.IO_REPARSE_TAG_MOUNT_POINT:
                        is_symlink = False
                if is_symlink:
                    linkto = os.readlink(srcname)
                    if symlinks:
                        # We can't just leave it to `copy_function` because legacy
                        # code with a custom `copy_function` may rely on copytree
                        # doing the right thing.
                        os.symlink(linkto, dstname)
                        copystat(srcobj, dstname, follow_symlinks=not symlinks)
                    else:
                        # ignore dangling symlink if the flag is on
                        if not os.path.exists(linkto) and ignore_dangling_symlinks:
                            continue
                        # otherwise let the copy occur. copy2 will raise an error
                        if srcentry.is_dir():
                            copytree(srcobj, dstname, symlinks, ignore,
                                     copy_function, dirs_exist_ok=dirs_exist_ok)
                        else:
                            copy_function(srcobj, dstname)
                elif srcentry.is_dir():
                    copytree(srcobj, dstname, symlinks, ignore, copy_function,
                             dirs_exist_ok=dirs_exist_ok)
                else:
                    # Will raise a SpecialFileError for unsupported file types
                    copy_function(srcobj, dstname)
            # catch the Error from the recursive copytree so that we can
            # continue with other files
            except Error as err:
                errors.extend(err.args[0])
            except OSError as why:
                errors.append((srcname, dstname, str(why)))
        try:
            copystat(src, dst)
        except OSError as why:
            # Copying file access times may fail on Windows
            if getattr(why, 'winerror', None) is None:
                errors.append((src, dst, str(why)))
        if errors:
>           raise Error(errors)
E           shutil.Error: [('/tmp/guix-build-python-docrepr-0.2.0.drv-0/tmpev83mper/_static/plot_directive.css', '/tmp/guix-build-python-docrepr-0.2.0.drv-0/docrepr-None/tmpbm5c0r1l/_static/plot_directive.css', "[Errno 13] Permission denied: '/tmp/guix-build-python-docrepr-0.2.0.drv-0/docrepr-None/tmpbm5c0r1l/_static/plot_directive.css'")]

/gnu/store/b6j1qw1a5rkbfvcy7lc9fm95abbzpa4x-python-3.9.9/lib/python3.9/shutil.py:522: Error

I've fixed it by changing except TypeError to except (TypeError, shutil.Error) (which means the copying falls back to the manual routine below the shutil.copytree failed call). A better approach could be to ensure all files are writable after creating the Sphinx object.

@CAM-Gerlach CAM-Gerlach added this to the v0.2.1 milestone Apr 15, 2022
@CAM-Gerlach
Copy link
Member

I've fixed it by changing except TypeError to except (TypeError, shutil.Error) (which means the copying falls back to the manual routine below the shutil.copytree failed call).

That seems reasonable for now, what do you think @martinRenou ? @Apteryks , would you like to submit a PR with that change?

Apteryks added a commit to Apteryks/docrepr that referenced this issue May 22, 2022
Fixes spyder-ide#54.

* docrepr/utils.py (merge_directories): Also handle 'shutil.Error'
exceptions.
@ccordoba12 ccordoba12 changed the title permission denied error when sphinx template files are read-only Permission denied error when sphinx template files are read-only May 28, 2022
Apteryks added a commit to Apteryks/docrepr that referenced this issue Jun 11, 2022
Fixes spyder-ide#54.

* docrepr/utils.py (merge_directories): Also handle 'shutil.Error'
exceptions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants