We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Theme files override docs_dir files on Windows
As investigated and discussed in https://github.com/squidfunk/mkdocs-material/issues/1282, custom files in docs_dir are overriden by theme files. 

For example, if you use the default MkDocs theme and create a custom `favicon.ico` in `docs/img`, the custom icon does not appear in the output. It is overwritten by the theme's own favicon.

The issue was already brought up in https://github.com/mkdocs/mkdocs/issues/1671 and fixed by https://github.com/mkdocs/mkdocs/pull/1672.

However, the fix (https://github.com/mkdocs/mkdocs/pull/1672) **only works on Unix systems (including Mac). It does not work on Windows.**

Windows handles path names differently, so I suppose the issue must be caused by that. See especially the use of `os.path` in `files.py` (https://github.com/mkdocs/mkdocs/commit/f8ac3c7ee).

I had a similar issue in mkdocs-exclude, see here: https://github.com/apenwarr/mkdocs-exclude/issues/2


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mkdocs/structure/files.py`
Content:
```
1 # coding: utf-8
2 
3 from __future__ import unicode_literals
4 import fnmatch
5 import os
6 import logging
7 from functools import cmp_to_key
8 
9 from mkdocs import utils
10 
11 
12 log = logging.getLogger(__name__)
13 log.addFilter(utils.warning_filter)
14 
15 
16 class Files(object):
17     """ A collection of File objects. """
18     def __init__(self, files):
19         self._files = files
20         self.src_paths = {file.src_path: file for file in files}
21 
22     def __iter__(self):
23         return iter(self._files)
24 
25     def __len__(self):
26         return len(self._files)
27 
28     def __contains__(self, path):
29         return path in self.src_paths
30 
31     def get_file_from_path(self, path):
32         """ Return a File instance with File.src_path equal to path. """
33         return self.src_paths.get(os.path.normpath(path))
34 
35     def append(self, file):
36         """ Append file to Files collection. """
37         self._files.append(file)
38         self.src_paths[file.src_path] = file
39 
40     def copy_static_files(self, dirty=False):
41         """ Copy static files from source to destination. """
42         for file in self:
43             if not file.is_documentation_page():
44                 file.copy_file(dirty)
45 
46     def documentation_pages(self):
47         """ Return iterable of all Markdown page file objects. """
48         return [file for file in self if file.is_documentation_page()]
49 
50     def static_pages(self):
51         """ Return iterable of all static page file objects. """
52         return [file for file in self if file.is_static_page()]
53 
54     def media_files(self):
55         """ Return iterable of all file objects which are not documentation or static pages. """
56         return [file for file in self if file.is_media_file()]
57 
58     def javascript_files(self):
59         """ Return iterable of all javascript file objects. """
60         return [file for file in self if file.is_javascript()]
61 
62     def css_files(self):
63         """ Return iterable of all CSS file objects. """
64         return [file for file in self if file.is_css()]
65 
66     def add_files_from_theme(self, env, config):
67         """ Retrieve static files from Jinja environment and add to collection. """
68         def filter(name):
69             patterns = ['.*', '*.py', '*.pyc', '*.html', '*readme*', 'mkdocs_theme.yml']
70             patterns.extend('*{0}'.format(x) for x in utils.markdown_extensions)
71             patterns.extend(config['theme'].static_templates)
72             for pattern in patterns:
73                 if fnmatch.fnmatch(name.lower(), pattern):
74                     return False
75             return True
76         for path in env.list_templates(filter_func=filter):
77             # Theme files do not override docs_dir files
78             if path not in self:
79                 for dir in config['theme'].dirs:
80                     # Find the first theme dir which contains path
81                     if os.path.isfile(os.path.join(dir, path)):
82                         self.append(File(path, dir, config['site_dir'], config['use_directory_urls']))
83                         break
84 
85 
86 class File(object):
87     """
88     A MkDocs File object.
89 
90     Points to the source and destination locations of a file.
91 
92     The `path` argument must be a path that exists relative to `src_dir`.
93 
94     The `src_dir` and `dest_dir` must be absolute paths on the local file system.
95 
96     The `use_directory_urls` argument controls how destination paths are generated. If `False`, a Markdown file is
97     mapped to an HTML file of the same name (the file extension is changed to `.html`). If True, a Markdown file is
98     mapped to an HTML index file (`index.html`) nested in a directory using the "name" of the file in `path`. The
99     `use_directory_urls` argument has no effect on non-Markdown files.
100 
101     File objects have the following properties, which are Unicode strings:
102 
103     File.src_path
104         The pure path of the source file relative to the source directory.
105 
106     File.abs_src_path
107         The absolute concrete path of the source file.
108 
109     File.dest_path
110         The pure path of the destination file relative to the destination directory.
111 
112     File.abs_dest_path
113         The absolute concrete path of the destination file.
114 
115     File.url
116         The url of the destination file relative to the destination directory as a string.
117     """
118     def __init__(self, path, src_dir, dest_dir, use_directory_urls):
119         self.page = None
120         self.src_path = os.path.normpath(path)
121         self.abs_src_path = os.path.normpath(os.path.join(src_dir, self.src_path))
122         self.name = self._get_stem()
123         self.dest_path = self._get_dest_path(use_directory_urls)
124         self.abs_dest_path = os.path.normpath(os.path.join(dest_dir, self.dest_path))
125         self.url = self._get_url(use_directory_urls)
126 
127     def __eq__(self, other):
128 
129         def sub_dict(d):
130             return dict((key, value) for key, value in d.items() if key in ['src_path', 'abs_src_path', 'url'])
131 
132         return (isinstance(other, self.__class__) and sub_dict(self.__dict__) == sub_dict(other.__dict__))
133 
134     def __ne__(self, other):
135         return not self.__eq__(other)
136 
137     def _get_stem(self):
138         """ Return the name of the file without it's extension. """
139         filename = os.path.basename(self.src_path)
140         stem, ext = os.path.splitext(filename)
141         return 'index' if stem in ('index', 'README') else stem
142 
143     def _get_dest_path(self, use_directory_urls):
144         """ Return destination path based on source path. """
145         if self.is_documentation_page():
146             if use_directory_urls:
147                 parent, filename = os.path.split(self.src_path)
148                 if self.name == 'index':
149                     # index.md or README.md => index.html
150                     return os.path.join(parent, 'index.html')
151                 else:
152                     # foo.md => foo/index.html
153                     return os.path.join(parent, self.name, 'index.html')
154             else:
155                 # foo.md => foo.html
156                 root, ext = os.path.splitext(self.src_path)
157                 return root + '.html'
158         return self.src_path
159 
160     def _get_url(self, use_directory_urls):
161         """ Return url based in destination path. """
162         url = self.dest_path.replace(os.path.sep, '/')
163         dirname, filename = os.path.split(url)
164         if use_directory_urls and filename == 'index.html':
165             if dirname == '':
166                 url = '.'
167             else:
168                 url = dirname + '/'
169         return utils.urlquote(url)
170 
171     def url_relative_to(self, other):
172         """ Return url for file relative to other file. """
173         return utils.get_relative_url(self.url, other.url if isinstance(other, File) else other)
174 
175     def copy_file(self, dirty=False):
176         """ Copy source file to destination, ensuring parent directories exist. """
177         if dirty and not self.is_modified():
178             log.debug("Skip copying unmodified file: '{}'".format(self.src_path))
179         else:
180             log.debug("Copying media file: '{}'".format(self.src_path))
181             utils.copy_file(self.abs_src_path, self.abs_dest_path)
182 
183     def is_modified(self):
184         if os.path.isfile(self.abs_dest_path):
185             return os.path.getmtime(self.abs_dest_path) < os.path.getmtime(self.abs_src_path)
186         return True
187 
188     def is_documentation_page(self):
189         """ Return True if file is a Markdown page. """
190         return os.path.splitext(self.src_path)[1] in utils.markdown_extensions
191 
192     def is_static_page(self):
193         """ Return True if file is a static page (html, xml, json). """
194         return os.path.splitext(self.src_path)[1] in (
195             '.html',
196             '.htm',
197             '.xml',
198             '.json',
199         )
200 
201     def is_media_file(self):
202         """ Return True if file is not a documentation or static page. """
203         return not (self.is_documentation_page() or self.is_static_page())
204 
205     def is_javascript(self):
206         """ Return True if file is a JavaScript file. """
207         return os.path.splitext(self.src_path)[1] in (
208             '.js',
209             '.javascript',
210         )
211 
212     def is_css(self):
213         """ Return True if file is a CSS file. """
214         return os.path.splitext(self.src_path)[1] in (
215             '.css',
216         )
217 
218 
219 def get_files(config):
220     """ Walk the `docs_dir` and return a Files collection. """
221     files = []
222     exclude = ['.*', '/templates']
223 
224     for source_dir, dirnames, filenames in os.walk(config['docs_dir'], followlinks=True):
225         relative_dir = os.path.relpath(source_dir, config['docs_dir'])
226 
227         for dirname in list(dirnames):
228             path = os.path.normpath(os.path.join(relative_dir, dirname))
229             # Skip any excluded directories
230             if _filter_paths(basename=dirname, path=path, is_dir=True, exclude=exclude):
231                 dirnames.remove(dirname)
232         dirnames.sort()
233 
234         for filename in _sort_files(filenames):
235             path = os.path.normpath(os.path.join(relative_dir, filename))
236             # Skip any excluded files
237             if _filter_paths(basename=filename, path=path, is_dir=False, exclude=exclude):
238                 continue
239             # Skip README.md if an index file also exists in dir
240             if filename.lower() == 'readme.md' and 'index.md' in filenames:
241                 log.warning("Both index.md and readme.md found. Skipping readme.md from {}".format(source_dir))
242                 continue
243             files.append(File(path, config['docs_dir'], config['site_dir'], config['use_directory_urls']))
244 
245     return Files(files)
246 
247 
248 def _sort_files(filenames):
249     """ Always sort `index` or `README` as first filename in list. """
250 
251     def compare(x, y):
252         if x == y:
253             return 0
254         if os.path.splitext(y)[0] in ['index', 'README']:
255             return 1
256         if os.path.splitext(x)[0] in ['index', 'README'] or x < y:
257             return -1
258         return 1
259 
260     return sorted(filenames, key=cmp_to_key(compare))
261 
262 
263 def _filter_paths(basename, path, is_dir, exclude):
264     """ .gitignore style file filtering. """
265     for item in exclude:
266         # Items ending in '/' apply only to directories.
267         if item.endswith('/') and not is_dir:
268             continue
269         # Items starting with '/' apply to the whole path.
270         # In any other cases just the basename is used.
271         match = path if item.startswith('/') else basename
272         if fnmatch.fnmatch(match, item.strip('/')):
273             return True
274     return False
275
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/mkdocs/structure/files.py b/mkdocs/structure/files.py
--- a/mkdocs/structure/files.py
+++ b/mkdocs/structure/files.py
@@ -75,6 +75,7 @@
             return True
         for path in env.list_templates(filter_func=filter):
             # Theme files do not override docs_dir files
+            path = os.path.normpath(path)
             if path not in self:
                 for dir in config['theme'].dirs:
                     # Find the first theme dir which contains path

{"golden_diff": "diff --git a/mkdocs/structure/files.py b/mkdocs/structure/files.py\n--- a/mkdocs/structure/files.py\n+++ b/mkdocs/structure/files.py\n@@ -75,6 +75,7 @@\n             return True\n         for path in env.list_templates(filter_func=filter):\n             # Theme files do not override docs_dir files\n+            path = os.path.normpath(path)\n             if path not in self:\n                 for dir in config['theme'].dirs:\n                     # Find the first theme dir which contains path\n", "issue": "Theme files override docs_dir files on Windows\nAs investigated and discussed in https://github.com/squidfunk/mkdocs-material/issues/1282, custom files in docs_dir are overriden by theme files. \r\n\r\nFor example, if you use the default MkDocs theme and create a custom `favicon.ico` in `docs/img`, the custom icon does not appear in the output. It is overwritten by the theme's own favicon.\r\n\r\nThe issue was already brought up in https://github.com/mkdocs/mkdocs/issues/1671 and fixed by https://github.com/mkdocs/mkdocs/pull/1672.\r\n\r\nHowever, the fix (https://github.com/mkdocs/mkdocs/pull/1672) **only works on Unix systems (including Mac). It does not work on Windows.**\r\n\r\nWindows handles path names differently, so I suppose the issue must be caused by that. See especially the use of `os.path` in `files.py` (https://github.com/mkdocs/mkdocs/commit/f8ac3c7ee).\r\n\r\nI had a similar issue in mkdocs-exclude, see here: https://github.com/apenwarr/mkdocs-exclude/issues/2\r\n\n", "before_files": [{"content": "# coding: utf-8\n\nfrom __future__ import unicode_literals\nimport fnmatch\nimport os\nimport logging\nfrom functools import cmp_to_key\n\nfrom mkdocs import utils\n\n\nlog = logging.getLogger(__name__)\nlog.addFilter(utils.warning_filter)\n\n\nclass Files(object):\n    \"\"\" A collection of File objects. \"\"\"\n    def __init__(self, files):\n        self._files = files\n        self.src_paths = {file.src_path: file for file in files}\n\n    def __iter__(self):\n        return iter(self._files)\n\n    def __len__(self):\n        return len(self._files)\n\n    def __contains__(self, path):\n        return path in self.src_paths\n\n    def get_file_from_path(self, path):\n        \"\"\" Return a File instance with File.src_path equal to path. \"\"\"\n        return self.src_paths.get(os.path.normpath(path))\n\n    def append(self, file):\n        \"\"\" Append file to Files collection. \"\"\"\n        self._files.append(file)\n        self.src_paths[file.src_path] = file\n\n    def copy_static_files(self, dirty=False):\n        \"\"\" Copy static files from source to destination. \"\"\"\n        for file in self:\n            if not file.is_documentation_page():\n                file.copy_file(dirty)\n\n    def documentation_pages(self):\n        \"\"\" Return iterable of all Markdown page file objects. \"\"\"\n        return [file for file in self if file.is_documentation_page()]\n\n    def static_pages(self):\n        \"\"\" Return iterable of all static page file objects. \"\"\"\n        return [file for file in self if file.is_static_page()]\n\n    def media_files(self):\n        \"\"\" Return iterable of all file objects which are not documentation or static pages. \"\"\"\n        return [file for file in self if file.is_media_file()]\n\n    def javascript_files(self):\n        \"\"\" Return iterable of all javascript file objects. \"\"\"\n        return [file for file in self if file.is_javascript()]\n\n    def css_files(self):\n        \"\"\" Return iterable of all CSS file objects. \"\"\"\n        return [file for file in self if file.is_css()]\n\n    def add_files_from_theme(self, env, config):\n        \"\"\" Retrieve static files from Jinja environment and add to collection. \"\"\"\n        def filter(name):\n            patterns = ['.*', '*.py', '*.pyc', '*.html', '*readme*', 'mkdocs_theme.yml']\n            patterns.extend('*{0}'.format(x) for x in utils.markdown_extensions)\n            patterns.extend(config['theme'].static_templates)\n            for pattern in patterns:\n                if fnmatch.fnmatch(name.lower(), pattern):\n                    return False\n            return True\n        for path in env.list_templates(filter_func=filter):\n            # Theme files do not override docs_dir files\n            if path not in self:\n                for dir in config['theme'].dirs:\n                    # Find the first theme dir which contains path\n                    if os.path.isfile(os.path.join(dir, path)):\n                        self.append(File(path, dir, config['site_dir'], config['use_directory_urls']))\n                        break\n\n\nclass File(object):\n    \"\"\"\n    A MkDocs File object.\n\n    Points to the source and destination locations of a file.\n\n    The `path` argument must be a path that exists relative to `src_dir`.\n\n    The `src_dir` and `dest_dir` must be absolute paths on the local file system.\n\n    The `use_directory_urls` argument controls how destination paths are generated. If `False`, a Markdown file is\n    mapped to an HTML file of the same name (the file extension is changed to `.html`). If True, a Markdown file is\n    mapped to an HTML index file (`index.html`) nested in a directory using the \"name\" of the file in `path`. The\n    `use_directory_urls` argument has no effect on non-Markdown files.\n\n    File objects have the following properties, which are Unicode strings:\n\n    File.src_path\n        The pure path of the source file relative to the source directory.\n\n    File.abs_src_path\n        The absolute concrete path of the source file.\n\n    File.dest_path\n        The pure path of the destination file relative to the destination directory.\n\n    File.abs_dest_path\n        The absolute concrete path of the destination file.\n\n    File.url\n        The url of the destination file relative to the destination directory as a string.\n    \"\"\"\n    def __init__(self, path, src_dir, dest_dir, use_directory_urls):\n        self.page = None\n        self.src_path = os.path.normpath(path)\n        self.abs_src_path = os.path.normpath(os.path.join(src_dir, self.src_path))\n        self.name = self._get_stem()\n        self.dest_path = self._get_dest_path(use_directory_urls)\n        self.abs_dest_path = os.path.normpath(os.path.join(dest_dir, self.dest_path))\n        self.url = self._get_url(use_directory_urls)\n\n    def __eq__(self, other):\n\n        def sub_dict(d):\n            return dict((key, value) for key, value in d.items() if key in ['src_path', 'abs_src_path', 'url'])\n\n        return (isinstance(other, self.__class__) and sub_dict(self.__dict__) == sub_dict(other.__dict__))\n\n    def __ne__(self, other):\n        return not self.__eq__(other)\n\n    def _get_stem(self):\n        \"\"\" Return the name of the file without it's extension. \"\"\"\n        filename = os.path.basename(self.src_path)\n        stem, ext = os.path.splitext(filename)\n        return 'index' if stem in ('index', 'README') else stem\n\n    def _get_dest_path(self, use_directory_urls):\n        \"\"\" Return destination path based on source path. \"\"\"\n        if self.is_documentation_page():\n            if use_directory_urls:\n                parent, filename = os.path.split(self.src_path)\n                if self.name == 'index':\n                    # index.md or README.md => index.html\n                    return os.path.join(parent, 'index.html')\n                else:\n                    # foo.md => foo/index.html\n                    return os.path.join(parent, self.name, 'index.html')\n            else:\n                # foo.md => foo.html\n                root, ext = os.path.splitext(self.src_path)\n                return root + '.html'\n        return self.src_path\n\n    def _get_url(self, use_directory_urls):\n        \"\"\" Return url based in destination path. \"\"\"\n        url = self.dest_path.replace(os.path.sep, '/')\n        dirname, filename = os.path.split(url)\n        if use_directory_urls and filename == 'index.html':\n            if dirname == '':\n                url = '.'\n            else:\n                url = dirname + '/'\n        return utils.urlquote(url)\n\n    def url_relative_to(self, other):\n        \"\"\" Return url for file relative to other file. \"\"\"\n        return utils.get_relative_url(self.url, other.url if isinstance(other, File) else other)\n\n    def copy_file(self, dirty=False):\n        \"\"\" Copy source file to destination, ensuring parent directories exist. \"\"\"\n        if dirty and not self.is_modified():\n            log.debug(\"Skip copying unmodified file: '{}'\".format(self.src_path))\n        else:\n            log.debug(\"Copying media file: '{}'\".format(self.src_path))\n            utils.copy_file(self.abs_src_path, self.abs_dest_path)\n\n    def is_modified(self):\n        if os.path.isfile(self.abs_dest_path):\n            return os.path.getmtime(self.abs_dest_path) < os.path.getmtime(self.abs_src_path)\n        return True\n\n    def is_documentation_page(self):\n        \"\"\" Return True if file is a Markdown page. \"\"\"\n        return os.path.splitext(self.src_path)[1] in utils.markdown_extensions\n\n    def is_static_page(self):\n        \"\"\" Return True if file is a static page (html, xml, json). \"\"\"\n        return os.path.splitext(self.src_path)[1] in (\n            '.html',\n            '.htm',\n            '.xml',\n            '.json',\n        )\n\n    def is_media_file(self):\n        \"\"\" Return True if file is not a documentation or static page. \"\"\"\n        return not (self.is_documentation_page() or self.is_static_page())\n\n    def is_javascript(self):\n        \"\"\" Return True if file is a JavaScript file. \"\"\"\n        return os.path.splitext(self.src_path)[1] in (\n            '.js',\n            '.javascript',\n        )\n\n    def is_css(self):\n        \"\"\" Return True if file is a CSS file. \"\"\"\n        return os.path.splitext(self.src_path)[1] in (\n            '.css',\n        )\n\n\ndef get_files(config):\n    \"\"\" Walk the `docs_dir` and return a Files collection. \"\"\"\n    files = []\n    exclude = ['.*', '/templates']\n\n    for source_dir, dirnames, filenames in os.walk(config['docs_dir'], followlinks=True):\n        relative_dir = os.path.relpath(source_dir, config['docs_dir'])\n\n        for dirname in list(dirnames):\n            path = os.path.normpath(os.path.join(relative_dir, dirname))\n            # Skip any excluded directories\n            if _filter_paths(basename=dirname, path=path, is_dir=True, exclude=exclude):\n                dirnames.remove(dirname)\n        dirnames.sort()\n\n        for filename in _sort_files(filenames):\n            path = os.path.normpath(os.path.join(relative_dir, filename))\n            # Skip any excluded files\n            if _filter_paths(basename=filename, path=path, is_dir=False, exclude=exclude):\n                continue\n            # Skip README.md if an index file also exists in dir\n            if filename.lower() == 'readme.md' and 'index.md' in filenames:\n                log.warning(\"Both index.md and readme.md found. Skipping readme.md from {}\".format(source_dir))\n                continue\n            files.append(File(path, config['docs_dir'], config['site_dir'], config['use_directory_urls']))\n\n    return Files(files)\n\n\ndef _sort_files(filenames):\n    \"\"\" Always sort `index` or `README` as first filename in list. \"\"\"\n\n    def compare(x, y):\n        if x == y:\n            return 0\n        if os.path.splitext(y)[0] in ['index', 'README']:\n            return 1\n        if os.path.splitext(x)[0] in ['index', 'README'] or x < y:\n            return -1\n        return 1\n\n    return sorted(filenames, key=cmp_to_key(compare))\n\n\ndef _filter_paths(basename, path, is_dir, exclude):\n    \"\"\" .gitignore style file filtering. \"\"\"\n    for item in exclude:\n        # Items ending in '/' apply only to directories.\n        if item.endswith('/') and not is_dir:\n            continue\n        # Items starting with '/' apply to the whole path.\n        # In any other cases just the basename is used.\n        match = path if item.startswith('/') else basename\n        if fnmatch.fnmatch(match, item.strip('/')):\n            return True\n    return False\n", "path": "mkdocs/structure/files.py"}], "after_files": [{"content": "# coding: utf-8\n\nfrom __future__ import unicode_literals\nimport fnmatch\nimport os\nimport logging\nfrom functools import cmp_to_key\n\nfrom mkdocs import utils\n\n\nlog = logging.getLogger(__name__)\nlog.addFilter(utils.warning_filter)\n\n\nclass Files(object):\n    \"\"\" A collection of File objects. \"\"\"\n    def __init__(self, files):\n        self._files = files\n        self.src_paths = {file.src_path: file for file in files}\n\n    def __iter__(self):\n        return iter(self._files)\n\n    def __len__(self):\n        return len(self._files)\n\n    def __contains__(self, path):\n        return path in self.src_paths\n\n    def get_file_from_path(self, path):\n        \"\"\" Return a File instance with File.src_path equal to path. \"\"\"\n        return self.src_paths.get(os.path.normpath(path))\n\n    def append(self, file):\n        \"\"\" Append file to Files collection. \"\"\"\n        self._files.append(file)\n        self.src_paths[file.src_path] = file\n\n    def copy_static_files(self, dirty=False):\n        \"\"\" Copy static files from source to destination. \"\"\"\n        for file in self:\n            if not file.is_documentation_page():\n                file.copy_file(dirty)\n\n    def documentation_pages(self):\n        \"\"\" Return iterable of all Markdown page file objects. \"\"\"\n        return [file for file in self if file.is_documentation_page()]\n\n    def static_pages(self):\n        \"\"\" Return iterable of all static page file objects. \"\"\"\n        return [file for file in self if file.is_static_page()]\n\n    def media_files(self):\n        \"\"\" Return iterable of all file objects which are not documentation or static pages. \"\"\"\n        return [file for file in self if file.is_media_file()]\n\n    def javascript_files(self):\n        \"\"\" Return iterable of all javascript file objects. \"\"\"\n        return [file for file in self if file.is_javascript()]\n\n    def css_files(self):\n        \"\"\" Return iterable of all CSS file objects. \"\"\"\n        return [file for file in self if file.is_css()]\n\n    def add_files_from_theme(self, env, config):\n        \"\"\" Retrieve static files from Jinja environment and add to collection. \"\"\"\n        def filter(name):\n            patterns = ['.*', '*.py', '*.pyc', '*.html', '*readme*', 'mkdocs_theme.yml']\n            patterns.extend('*{0}'.format(x) for x in utils.markdown_extensions)\n            patterns.extend(config['theme'].static_templates)\n            for pattern in patterns:\n                if fnmatch.fnmatch(name.lower(), pattern):\n                    return False\n            return True\n        for path in env.list_templates(filter_func=filter):\n            # Theme files do not override docs_dir files\n            path = os.path.normpath(path)\n            if path not in self:\n                for dir in config['theme'].dirs:\n                    # Find the first theme dir which contains path\n                    if os.path.isfile(os.path.join(dir, path)):\n                        self.append(File(path, dir, config['site_dir'], config['use_directory_urls']))\n                        break\n\n\nclass File(object):\n    \"\"\"\n    A MkDocs File object.\n\n    Points to the source and destination locations of a file.\n\n    The `path` argument must be a path that exists relative to `src_dir`.\n\n    The `src_dir` and `dest_dir` must be absolute paths on the local file system.\n\n    The `use_directory_urls` argument controls how destination paths are generated. If `False`, a Markdown file is\n    mapped to an HTML file of the same name (the file extension is changed to `.html`). If True, a Markdown file is\n    mapped to an HTML index file (`index.html`) nested in a directory using the \"name\" of the file in `path`. The\n    `use_directory_urls` argument has no effect on non-Markdown files.\n\n    File objects have the following properties, which are Unicode strings:\n\n    File.src_path\n        The pure path of the source file relative to the source directory.\n\n    File.abs_src_path\n        The absolute concrete path of the source file.\n\n    File.dest_path\n        The pure path of the destination file relative to the destination directory.\n\n    File.abs_dest_path\n        The absolute concrete path of the destination file.\n\n    File.url\n        The url of the destination file relative to the destination directory as a string.\n    \"\"\"\n    def __init__(self, path, src_dir, dest_dir, use_directory_urls):\n        self.page = None\n        self.src_path = os.path.normpath(path)\n        self.abs_src_path = os.path.normpath(os.path.join(src_dir, self.src_path))\n        self.name = self._get_stem()\n        self.dest_path = self._get_dest_path(use_directory_urls)\n        self.abs_dest_path = os.path.normpath(os.path.join(dest_dir, self.dest_path))\n        self.url = self._get_url(use_directory_urls)\n\n    def __eq__(self, other):\n\n        def sub_dict(d):\n            return dict((key, value) for key, value in d.items() if key in ['src_path', 'abs_src_path', 'url'])\n\n        return (isinstance(other, self.__class__) and sub_dict(self.__dict__) == sub_dict(other.__dict__))\n\n    def __ne__(self, other):\n        return not self.__eq__(other)\n\n    def _get_stem(self):\n        \"\"\" Return the name of the file without it's extension. \"\"\"\n        filename = os.path.basename(self.src_path)\n        stem, ext = os.path.splitext(filename)\n        return 'index' if stem in ('index', 'README') else stem\n\n    def _get_dest_path(self, use_directory_urls):\n        \"\"\" Return destination path based on source path. \"\"\"\n        if self.is_documentation_page():\n            if use_directory_urls:\n                parent, filename = os.path.split(self.src_path)\n                if self.name == 'index':\n                    # index.md or README.md => index.html\n                    return os.path.join(parent, 'index.html')\n                else:\n                    # foo.md => foo/index.html\n                    return os.path.join(parent, self.name, 'index.html')\n            else:\n                # foo.md => foo.html\n                root, ext = os.path.splitext(self.src_path)\n                return root + '.html'\n        return self.src_path\n\n    def _get_url(self, use_directory_urls):\n        \"\"\" Return url based in destination path. \"\"\"\n        url = self.dest_path.replace(os.path.sep, '/')\n        dirname, filename = os.path.split(url)\n        if use_directory_urls and filename == 'index.html':\n            if dirname == '':\n                url = '.'\n            else:\n                url = dirname + '/'\n        return utils.urlquote(url)\n\n    def url_relative_to(self, other):\n        \"\"\" Return url for file relative to other file. \"\"\"\n        return utils.get_relative_url(self.url, other.url if isinstance(other, File) else other)\n\n    def copy_file(self, dirty=False):\n        \"\"\" Copy source file to destination, ensuring parent directories exist. \"\"\"\n        if dirty and not self.is_modified():\n            log.debug(\"Skip copying unmodified file: '{}'\".format(self.src_path))\n        else:\n            log.debug(\"Copying media file: '{}'\".format(self.src_path))\n            utils.copy_file(self.abs_src_path, self.abs_dest_path)\n\n    def is_modified(self):\n        if os.path.isfile(self.abs_dest_path):\n            return os.path.getmtime(self.abs_dest_path) < os.path.getmtime(self.abs_src_path)\n        return True\n\n    def is_documentation_page(self):\n        \"\"\" Return True if file is a Markdown page. \"\"\"\n        return os.path.splitext(self.src_path)[1] in utils.markdown_extensions\n\n    def is_static_page(self):\n        \"\"\" Return True if file is a static page (html, xml, json). \"\"\"\n        return os.path.splitext(self.src_path)[1] in (\n            '.html',\n            '.htm',\n            '.xml',\n            '.json',\n        )\n\n    def is_media_file(self):\n        \"\"\" Return True if file is not a documentation or static page. \"\"\"\n        return not (self.is_documentation_page() or self.is_static_page())\n\n    def is_javascript(self):\n        \"\"\" Return True if file is a JavaScript file. \"\"\"\n        return os.path.splitext(self.src_path)[1] in (\n            '.js',\n            '.javascript',\n        )\n\n    def is_css(self):\n        \"\"\" Return True if file is a CSS file. \"\"\"\n        return os.path.splitext(self.src_path)[1] in (\n            '.css',\n        )\n\n\ndef get_files(config):\n    \"\"\" Walk the `docs_dir` and return a Files collection. \"\"\"\n    files = []\n    exclude = ['.*', '/templates']\n\n    for source_dir, dirnames, filenames in os.walk(config['docs_dir'], followlinks=True):\n        relative_dir = os.path.relpath(source_dir, config['docs_dir'])\n\n        for dirname in list(dirnames):\n            path = os.path.normpath(os.path.join(relative_dir, dirname))\n            # Skip any excluded directories\n            if _filter_paths(basename=dirname, path=path, is_dir=True, exclude=exclude):\n                dirnames.remove(dirname)\n        dirnames.sort()\n\n        for filename in _sort_files(filenames):\n            path = os.path.normpath(os.path.join(relative_dir, filename))\n            # Skip any excluded files\n            if _filter_paths(basename=filename, path=path, is_dir=False, exclude=exclude):\n                continue\n            # Skip README.md if an index file also exists in dir\n            if filename.lower() == 'readme.md' and 'index.md' in filenames:\n                log.warning(\"Both index.md and readme.md found. Skipping readme.md from {}\".format(source_dir))\n                continue\n            files.append(File(path, config['docs_dir'], config['site_dir'], config['use_directory_urls']))\n\n    return Files(files)\n\n\ndef _sort_files(filenames):\n    \"\"\" Always sort `index` or `README` as first filename in list. \"\"\"\n\n    def compare(x, y):\n        if x == y:\n            return 0\n        if os.path.splitext(y)[0] in ['index', 'README']:\n            return 1\n        if os.path.splitext(x)[0] in ['index', 'README'] or x < y:\n            return -1\n        return 1\n\n    return sorted(filenames, key=cmp_to_key(compare))\n\n\ndef _filter_paths(basename, path, is_dir, exclude):\n    \"\"\" .gitignore style file filtering. \"\"\"\n    for item in exclude:\n        # Items ending in '/' apply only to directories.\n        if item.endswith('/') and not is_dir:\n            continue\n        # Items starting with '/' apply to the whole path.\n        # In any other cases just the basename is used.\n        match = path if item.startswith('/') else basename\n        if fnmatch.fnmatch(match, item.strip('/')):\n            return True\n    return False\n", "path": "mkdocs/structure/files.py"}]}