landscape-bot: [![Code Health](https://landscape.io/badge/198248/landscape.svg?style=flat)](https://landscape.io/diff/187394)
Code quality remained the same when pulling **[a5ab2e1](https://github.com/d0ugal/mkdocs/commit/a5ab2e12e22a199617fcffd129d4762bfc6b712b) on d0ugal:symlink** into **[98814de](https://github.com/mkdocs/mkdocs/commit/98814de505c9d8f1849292372eff1c5ae492261c) on mkdocs:master**.
diff --git a/mkdocs/config/config_options.py b/mkdocs/config/config_options.py
index 38a61442..28ee62c8 100644
--- a/mkdocs/config/config_options.py
+++ b/mkdocs/config/config_options.py
@@ -332,6 +332,13 @@ class Extras(OptionallyRequired):
             dirs.sort()
             for filename in sorted(filenames):
                 fullpath = os.path.join(dirpath, filename)
+
+                # Some editors (namely Emacs) will create temporary symlinks
+                # for internal magic. We can just ignore these files.
+                if os.path.islink(fullpath):
+                    if not os.path.exists(os.readlink(fullpath)):
+                        continue
+
                 relpath = os.path.normpath(os.path.relpath(fullpath, docs_dir))
                 if self.file_match(relpath):
                     yield relpath

Ignore broken symlinks in mkdocs serve.
What I am experiencing:

* When I am editing `index.md` in Emacs, Emacs creates files like:

```
➜  docs git:(master) ✗ ls -al .#*  
lrwxrwxrwx 1 paulproteus paulproteus 36 Jun 17 17:24 .#index.md -> paulproteus@charkha.27783:1434311808
```

* These files are Emacs' way of using symlinks to track which computer+process was Emacs-ing the file, so that in case of a crash, Emacs can figure out how to restore its state.

What I expect:

* When I edit `somethingelse.md` and press save, I expect the mkdocs livereload to reload the browser.

What I see instead:

```
INFO    -  Building documentation... 
ERROR   -  file not found: /home/paulproteus/projects/sandstorm/docs/.#index.md 
ERROR   -  Error building page .#index.md 
[E 150617 17:22:21 ioloop:612] Exception in callback (3, <function null_wrapper at 0x7fc883190500>)
    Traceback (most recent call last):
      File "/home/paulproteus/.local/lib/python2.7/site-packages/tornado/ioloop.py", line 866, in start
        handler_func(fd_obj, events)
      File "/home/paulproteus/.local/lib/python2.7/site-packages/tornado/stack_context.py", line 275, in null_wrapper
        return fn(*args, **kwargs)
      File "/usr/lib/python2.7/dist-packages/pyinotify.py", line 1604, in handle_read
        self.process_events()
      File "/usr/lib/python2.7/dist-packages/pyinotify.py", line 1321, in process_events
        self._default_proc_fun(revent)
      File "/home/paulproteus/.local/lib/python2.7/site-packages/livereload/watcher.py", line 152, in inotify_event
        self.callback()
      File "/home/paulproteus/.local/lib/python2.7/site-packages/livereload/handlers.py", line 65, in poll_tasks
        filepath, delay = cls.watcher.examine()
      File "/home/paulproteus/.local/lib/python2.7/site-packages/livereload/watcher.py", line 72, in examine
        func and func()
      File "/usr/lib/python2.7/dist-packages/mkdocs/serve.py", line 74, in builder
        build(config, live_server=True)
      File "/usr/lib/python2.7/dist-packages/mkdocs/build.py", line 299, in build
        build_pages(config)
      File "/usr/lib/python2.7/dist-packages/mkdocs/build.py", line 259, in build_pages
        dump_json)
      File "/usr/lib/python2.7/dist-packages/mkdocs/build.py", line 171, in _build_page
        input_content = io.open(input_path, 'r', encoding='utf-8').read()
    IOError: [Errno 2] No such file or directory: '/home/paulproteus/projects/sandstorm/docs/.#index.md'
```

What I propose:

* If a "No such file or directory" error occurs, but the problem is a broken symlink, the `mkdocs` build should continue as if the file does not exist. Note that this arguably is a special-case to handle Emacs' own weirdness; a different way to do it would be to look at the list of git ignored files.
* Perhaps in general, `mkdocs` should issue a warning (not an error) on broken symlinks, gracefully ignoring them.

I'm open to a bunch of ideas. I wanted to file this in the hopes of sparking a discussion where the maintainers of mkdocs could express their opinion about the best way forward.

Thanks so much! Also hi! I'm twitter.com/asheeshlaroia and was chatting with a mkdocs developer earlier today. Seems like a great project! I learned about it via http://ericholscher.com/blog/2014/feb/27/how-i-judge-documentation-quality/ !
diff --git a/mkdocs/tests/config/config_options_tests.py b/mkdocs/tests/config/config_options_tests.py
index b1e7bff7..d16f675b 100644
--- a/mkdocs/tests/config/config_options_tests.py
+++ b/mkdocs/tests/config/config_options_tests.py
@@ -1,6 +1,7 @@
 from __future__ import unicode_literals
 
 import os
+import tempfile
 import unittest
 
 from mkdocs import utils
@@ -251,6 +252,25 @@ class ExtrasTest(unittest.TestCase):
         self.assertRaises(config_options.ValidationError,
                           option.validate, {})
 
+    def test_talk(self):
+
+        option = config_options.Extras(utils.is_markdown_file)
+
+        tmp_dir = tempfile.mkdtemp()
+
+        f1 = os.path.join(tmp_dir, 'file1.md')
+        f2 = os.path.join(tmp_dir, 'file2.md')
+
+        open(f1, 'a').close()
+
+        # symlink isn't available on Python 2 on Windows.
+        if hasattr(os, 'symlink'):
+            os.symlink('/path/that/doesnt/exist', f2)
+
+        files = list(option.walk_docs_dir(tmp_dir))
+
+        self.assertEqual(['file1.md', ], files)
+
 
 class PagesTest(unittest.TestCase):
 

pip install -e .
parse_log_pytest
requirements.txt
pytest
apt-get update apt-get install -y gcc
3.9
requirements/project.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
click==8.1.8
exceptiongroup==1.2.2
ghp-import==2.1.0
importlib_metadata==8.6.1
iniconfig==2.1.0
Jinja2==3.1.6
livereload==2.7.1
Markdown==3.7
MarkupSafe==3.0.2
mergedeep==1.3.4
-e git+https://github.com/mkdocs/mkdocs.git@98814de505c9d8f1849292372eff1c5ae492261c#egg=mkdocs
mkdocs-bootstrap==0.2.0
mkdocs-bootswatch==0.5.0
mkdocs-get-deps==0.2.0
packaging==24.2
pathspec==0.12.1
platformdirs==4.3.7
pluggy==1.5.0
pytest==8.3.5
python-dateutil==2.9.0.post0
PyYAML==6.0.2
pyyaml_env_tag==0.1
six==1.17.0
tomli==2.2.1
tornado==6.4.2
watchdog==6.0.0
zipp==3.21.0

name: mkdocs
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - click==8.1.8
      - exceptiongroup==1.2.2
      - ghp-import==2.1.0
      - importlib-metadata==8.6.1
      - iniconfig==2.1.0
      - jinja2==3.1.6
      - livereload==2.7.1
      - markdown==3.7
      - markupsafe==3.0.2
      - mergedeep==1.3.4
      - mkdocs-bootstrap==0.2.0
      - mkdocs-bootswatch==0.5.0
      - mkdocs-get-deps==0.2.0
      - packaging==24.2
      - pathspec==0.12.1
      - platformdirs==4.3.7
      - pluggy==1.5.0
      - pytest==8.3.5
      - python-dateutil==2.9.0.post0
      - pyyaml==6.0.2
      - pyyaml-env-tag==0.1
      - six==1.17.0
      - tomli==2.2.1
      - tornado==6.4.2
      - watchdog==6.0.0
      - zipp==3.21.0
prefix: /opt/conda/envs/mkdocs

mkdocs/tests/config/config_options_tests.py::ExtrasTest::test_talk
mkdocs/tests/config/config_options_tests.py::OptionallyRequiredTest::test_default mkdocs/tests/config/config_options_tests.py::OptionallyRequiredTest::test_empty mkdocs/tests/config/config_options_tests.py::OptionallyRequiredTest::test_replace_default mkdocs/tests/config/config_options_tests.py::OptionallyRequiredTest::test_required mkdocs/tests/config/config_options_tests.py::OptionallyRequiredTest::test_required_no_default mkdocs/tests/config/config_options_tests.py::TypeTest::test_length mkdocs/tests/config/config_options_tests.py::TypeTest::test_multiple_types mkdocs/tests/config/config_options_tests.py::TypeTest::test_single_type mkdocs/tests/config/config_options_tests.py::URLTest::test_invalid mkdocs/tests/config/config_options_tests.py::URLTest::test_invalid_url mkdocs/tests/config/config_options_tests.py::URLTest::test_valid_url mkdocs/tests/config/config_options_tests.py::RepoURLTest::test_repo_name_bitbucket mkdocs/tests/config/config_options_tests.py::RepoURLTest::test_repo_name_custom mkdocs/tests/config/config_options_tests.py::RepoURLTest::test_repo_name_github mkdocs/tests/config/config_options_tests.py::DirTest::test_file mkdocs/tests/config/config_options_tests.py::DirTest::test_incorrect_type_attribute_error mkdocs/tests/config/config_options_tests.py::DirTest::test_incorrect_type_type_error mkdocs/tests/config/config_options_tests.py::DirTest::test_missing_dir mkdocs/tests/config/config_options_tests.py::DirTest::test_missing_dir_but_required mkdocs/tests/config/config_options_tests.py::DirTest::test_valid_dir mkdocs/tests/config/config_options_tests.py::SiteDirTest::test_doc_dir_in_site_dir mkdocs/tests/config/config_options_tests.py::SiteDirTest::test_site_dir_in_docs_dir mkdocs/tests/config/config_options_tests.py::ThemeTest::test_theme mkdocs/tests/config/config_options_tests.py::ThemeTest::test_theme_invalid mkdocs/tests/config/config_options_tests.py::ExtrasTest::test_empty mkdocs/tests/config/config_options_tests.py::ExtrasTest::test_invalid mkdocs/tests/config/config_options_tests.py::ExtrasTest::test_provided mkdocs/tests/config/config_options_tests.py::PagesTest::test_invalid_config mkdocs/tests/config/config_options_tests.py::PagesTest::test_invalid_type mkdocs/tests/config/config_options_tests.py::PagesTest::test_provided mkdocs/tests/config/config_options_tests.py::PagesTest::test_provided_dict mkdocs/tests/config/config_options_tests.py::PagesTest::test_provided_empty mkdocs/tests/config/config_options_tests.py::NumPagesTest::test_invalid_pages mkdocs/tests/config/config_options_tests.py::NumPagesTest::test_many_pages mkdocs/tests/config/config_options_tests.py::NumPagesTest::test_one_page mkdocs/tests/config/config_options_tests.py::NumPagesTest::test_provided mkdocs/tests/config/config_options_tests.py::PrivateTest::test_defined mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_builtins mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_builtins_config mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_configkey mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_duplicates mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_invalid_config_item mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_invalid_config_option mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_invalid_dict_item mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_list_dicts mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_mixed_list mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_none mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_not_list mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_simple_list
mkdocs/config/config_options.py
mkdocs/config/config_options.py