We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Please support SOURCE_DATE_EPOCH specification for build time stamps
Copied from http://bugs.debian.org/824266:

> Hi,
> 
> mkdocs integrates build time stamps into the documentation it is
> generating. This makes at least unburden-home-dir no more reproducibly
> building because it now uses mkdocs to generate HTML documentation at
> build time.
> 
> See https://tests.reproducible-builds.org/rb-pkg/unstable/amd64/unburden-home-dir.html and
> https://tests.reproducible-builds.org/dbd/unstable/amd64/unburden-home-dir_0.4.diffoscope.html
> 
> Other cases of this issue are the source package djangorestframework (see
> https://tests.reproducible-builds.org/rb-pkg/testing/i386/djangorestframework.html and
> https://tests.reproducible-builds.org/dbd/testing/i386/djangorestframework_3.3.2-2.diffoscope.html)
> and of course the source package python-mkdocs itself, see
> https://tests.reproducible-builds.org/rb-pkg/unstable/amd64/python-mkdocs.html and
> https://tests.reproducible-builds.org/dbd/unstable/amd64/python-mkdocs_0.15.3-3.diffoscope.html
> 
> At https://reproducible-builds.org/specs/source-date-epoch/ you can find
> a bound to be widely adopted specification for how to solve such kind of
> issues generally: If the environment variable SOURCE_DATE_EPOCH exists
> use the Unix time stamp (seconds since 1st of January 1970, 00:00:00
> UTC) it contains to preseed all build time stamps with that time stamp
> instead of the current time.
> 
> See https://wiki.debian.org/ReproducibleBuilds/TimestampsProposal#Examples
> for some examples on how to implement support for SOURCE_DATE_EPOCH,
> including an example for Python.


--- 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/nav.py`
Content:
```
1 # coding: utf-8
2 
3 """
4 Deals with generating the site-wide navigation.
5 
6 This consists of building a set of interlinked page and header objects.
7 """
8 
9 from __future__ import unicode_literals
10 import datetime
11 import logging
12 import os
13 
14 from mkdocs import utils, exceptions
15 
16 log = logging.getLogger(__name__)
17 
18 
19 def filename_to_title(filename):
20     """
21     Automatically generate a default title, given a filename.
22     """
23     if utils.is_homepage(filename):
24         return 'Home'
25 
26     return utils.filename_to_title(filename)
27 
28 
29 class SiteNavigation(object):
30     def __init__(self, pages_config, use_directory_urls=True):
31         self.url_context = URLContext()
32         self.file_context = FileContext()
33         self.nav_items, self.pages = _generate_site_navigation(
34             pages_config, self.url_context, use_directory_urls)
35         self.homepage = self.pages[0] if self.pages else None
36         self.use_directory_urls = use_directory_urls
37 
38     def __str__(self):
39         return ''.join([str(item) for item in self])
40 
41     def __iter__(self):
42         return iter(self.nav_items)
43 
44     def walk_pages(self):
45         """
46         Returns each page in the site in turn.
47 
48         Additionally this sets the active status of the pages and headers,
49         in the site navigation, so that the rendered navbar can correctly
50         highlight the currently active page and/or header item.
51         """
52         page = self.homepage
53         page.set_active()
54         self.url_context.set_current_url(page.abs_url)
55         self.file_context.set_current_path(page.input_path)
56         yield page
57         while page.next_page:
58             page.set_active(False)
59             page = page.next_page
60             page.set_active()
61             self.url_context.set_current_url(page.abs_url)
62             self.file_context.set_current_path(page.input_path)
63             yield page
64         page.set_active(False)
65 
66     @property
67     def source_files(self):
68         if not hasattr(self, '_source_files'):
69             self._source_files = set([page.input_path for page in self.pages])
70         return self._source_files
71 
72 
73 class URLContext(object):
74     """
75     The URLContext is used to ensure that we can generate the appropriate
76     relative URLs to other pages from any given page in the site.
77 
78     We use relative URLs so that static sites can be deployed to any location
79     without having to specify what the path component on the host will be
80     if the documentation is not hosted at the root path.
81     """
82 
83     def __init__(self):
84         self.base_path = '/'
85 
86     def set_current_url(self, current_url):
87         self.base_path = os.path.dirname(current_url)
88 
89     def make_relative(self, url):
90         """
91         Given a URL path return it as a relative URL,
92         given the context of the current page.
93         """
94         suffix = '/' if (url.endswith('/') and len(url) > 1) else ''
95         # Workaround for bug on `os.path.relpath()` in Python 2.6
96         if self.base_path == '/':
97             if url == '/':
98                 # Workaround for static assets
99                 return '.'
100             return url.lstrip('/')
101         # Under Python 2.6, relative_path adds an extra '/' at the end.
102         relative_path = os.path.relpath(url, start=self.base_path)
103         relative_path = relative_path.rstrip('/') + suffix
104 
105         return utils.path_to_url(relative_path)
106 
107 
108 class FileContext(object):
109     """
110     The FileContext is used to ensure that we can generate the appropriate
111     full path for other pages given their relative path from a particular page.
112 
113     This is used when we have relative hyperlinks in the documentation, so that
114     we can ensure that they point to markdown documents that actually exist
115     in the `pages` config.
116     """
117     def __init__(self):
118         self.current_file = None
119         self.base_path = ''
120 
121     def set_current_path(self, current_path):
122         self.current_file = current_path
123         self.base_path = os.path.dirname(current_path)
124 
125     def make_absolute(self, path):
126         """
127         Given a relative file path return it as a POSIX-style
128         absolute filepath, given the context of the current page.
129         """
130         return os.path.normpath(os.path.join(self.base_path, path))
131 
132 
133 class Page(object):
134     def __init__(self, title, url, path, url_context):
135 
136         self.title = title
137         self.abs_url = url
138         self.active = False
139         self.url_context = url_context
140         self.update_date = datetime.datetime.now().strftime("%Y-%m-%d")
141 
142         # Relative paths to the input markdown file and output html file.
143         self.input_path = path
144         self.output_path = utils.get_html_path(path)
145 
146         # Links to related pages
147         self.previous_page = None
148         self.next_page = None
149         self.ancestors = []
150 
151         # Placeholders to be filled in later in the build
152         # process when we have access to the config.
153         self.canonical_url = None
154         self.edit_url = None
155         self.content = None
156         self.meta = None
157         self.toc = None
158 
159     @property
160     def url(self):
161         return self.url_context.make_relative(self.abs_url)
162 
163     @property
164     def is_homepage(self):
165         return utils.is_homepage(self.input_path)
166 
167     @property
168     def is_top_level(self):
169         return len(self.ancestors) == 0
170 
171     def __str__(self):
172         return self.indent_print()
173 
174     def indent_print(self, depth=0):
175         indent = '    ' * depth
176         active_marker = ' [*]' if self.active else ''
177         title = self.title if (self.title is not None) else '[blank]'
178         return '%s%s - %s%s\n' % (indent, title, self.abs_url, active_marker)
179 
180     def set_active(self, active=True):
181         self.active = active
182         for ancestor in self.ancestors:
183             ancestor.set_active(active)
184 
185     def set_canonical_url(self, base):
186         if not base.endswith('/'):
187             base += '/'
188         self.canonical_url = utils.urljoin(base, self.abs_url.lstrip('/'))
189 
190     def set_edit_url(self, repo_url, edit_uri):
191         if not repo_url.endswith('/'):
192             repo_url += '/'
193         if not edit_uri:
194             self.edit_url = repo_url
195         else:
196             if not edit_uri.endswith('/'):
197                 edit_uri += '/'
198             self.edit_url = utils.urljoin(
199                 repo_url + edit_uri,
200                 self.input_path)
201 
202 
203 class Header(object):
204     def __init__(self, title, children):
205         self.title, self.children = title, children
206         self.active = False
207         self.ancestors = []
208 
209     def __str__(self):
210         return self.indent_print()
211 
212     @property
213     def is_top_level(self):
214         return len(self.ancestors) == 0
215 
216     def indent_print(self, depth=0):
217         indent = '    ' * depth
218         active_marker = ' [*]' if self.active else ''
219         ret = '%s%s%s\n' % (indent, self.title, active_marker)
220         for item in self.children:
221             ret += item.indent_print(depth + 1)
222         return ret
223 
224     def set_active(self, active=True):
225         self.active = active
226         for ancestor in self.ancestors:
227             ancestor.set_active(active)
228 
229 
230 def _path_to_page(path, title, url_context, use_directory_urls):
231     if title is None:
232         title = filename_to_title(path.split(os.path.sep)[-1])
233     url = utils.get_url_path(path, use_directory_urls)
234     return Page(title=title, url=url, path=path,
235                 url_context=url_context)
236 
237 
238 def _follow(config_line, url_context, use_dir_urls, header=None, title=None):
239 
240     if isinstance(config_line, utils.string_types):
241         path = os.path.normpath(config_line)
242         page = _path_to_page(path, title, url_context, use_dir_urls)
243 
244         if header:
245             page.ancestors = header.ancestors + [header, ]
246             header.children.append(page)
247 
248         yield page
249         raise StopIteration
250 
251     elif not isinstance(config_line, dict):
252         msg = ("Line in 'page' config is of type {0}, dict or string "
253                "expected. Config: {1}").format(type(config_line), config_line)
254         raise exceptions.ConfigurationError(msg)
255 
256     if len(config_line) > 1:
257         raise exceptions.ConfigurationError(
258             "Page configs should be in the format 'name: markdown.md'. The "
259             "config contains an invalid entry: {0}".format(config_line))
260     elif len(config_line) == 0:
261         log.warning("Ignoring empty line in the pages config.")
262         raise StopIteration
263 
264     next_cat_or_title, subpages_or_path = next(iter(config_line.items()))
265 
266     if isinstance(subpages_or_path, utils.string_types):
267         path = subpages_or_path
268         for sub in _follow(path, url_context, use_dir_urls, header=header, title=next_cat_or_title):
269             yield sub
270         raise StopIteration
271 
272     elif not isinstance(subpages_or_path, list):
273         msg = ("Line in 'page' config is of type {0}, list or string "
274                "expected for sub pages. Config: {1}"
275                ).format(type(config_line), config_line)
276         raise exceptions.ConfigurationError(msg)
277 
278     next_header = Header(title=next_cat_or_title, children=[])
279     if header:
280         next_header.ancestors = [header]
281         header.children.append(next_header)
282     yield next_header
283 
284     subpages = subpages_or_path
285 
286     for subpage in subpages:
287         for sub in _follow(subpage, url_context, use_dir_urls, next_header):
288             yield sub
289 
290 
291 def _generate_site_navigation(pages_config, url_context, use_dir_urls=True):
292     """
293     Returns a list of Page and Header instances that represent the
294     top level site navigation.
295     """
296     nav_items = []
297     pages = []
298 
299     previous = None
300 
301     for config_line in pages_config:
302 
303         for page_or_header in _follow(
304                 config_line, url_context, use_dir_urls):
305 
306             if isinstance(page_or_header, Header):
307 
308                 if page_or_header.is_top_level:
309                     nav_items.append(page_or_header)
310 
311             elif isinstance(page_or_header, Page):
312 
313                 if page_or_header.is_top_level:
314                     nav_items.append(page_or_header)
315 
316                 pages.append(page_or_header)
317 
318                 if previous:
319                     page_or_header.previous_page = previous
320                     previous.next_page = page_or_header
321                 previous = page_or_header
322 
323     if len(pages) == 0:
324         raise exceptions.ConfigurationError(
325             "No pages found in the pages config. "
326             "Remove it entirely to enable automatic page discovery.")
327 
328     return (nav_items, pages)
329
```
--- 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/nav.py b/mkdocs/nav.py
--- a/mkdocs/nav.py
+++ b/mkdocs/nav.py
@@ -137,7 +137,15 @@
         self.abs_url = url
         self.active = False
         self.url_context = url_context
-        self.update_date = datetime.datetime.now().strftime("%Y-%m-%d")
+
+        # Support SOURCE_DATE_EPOCH environment variable for "reproducible" builds.
+        # See https://reproducible-builds.org/specs/source-date-epoch/
+        if 'SOURCE_DATE_EPOCH' in os.environ:
+            self.update_date = datetime.datetime.utcfromtimestamp(
+                int(os.environ['SOURCE_DATE_EPOCH'])
+            ).strftime("%Y-%m-%d")
+        else:
+            self.update_date = datetime.datetime.now().strftime("%Y-%m-%d")
 
         # Relative paths to the input markdown file and output html file.
         self.input_path = path

{"golden_diff": "diff --git a/mkdocs/nav.py b/mkdocs/nav.py\n--- a/mkdocs/nav.py\n+++ b/mkdocs/nav.py\n@@ -137,7 +137,15 @@\n         self.abs_url = url\n         self.active = False\n         self.url_context = url_context\n-        self.update_date = datetime.datetime.now().strftime(\"%Y-%m-%d\")\n+\n+        # Support SOURCE_DATE_EPOCH environment variable for \"reproducible\" builds.\n+        # See https://reproducible-builds.org/specs/source-date-epoch/\n+        if 'SOURCE_DATE_EPOCH' in os.environ:\n+            self.update_date = datetime.datetime.utcfromtimestamp(\n+                int(os.environ['SOURCE_DATE_EPOCH'])\n+            ).strftime(\"%Y-%m-%d\")\n+        else:\n+            self.update_date = datetime.datetime.now().strftime(\"%Y-%m-%d\")\n \n         # Relative paths to the input markdown file and output html file.\n         self.input_path = path\n", "issue": "Please support SOURCE_DATE_EPOCH specification for build time stamps\nCopied from http://bugs.debian.org/824266:\n\n> Hi,\n> \n> mkdocs integrates build time stamps into the documentation it is\n> generating. This makes at least unburden-home-dir no more reproducibly\n> building because it now uses mkdocs to generate HTML documentation at\n> build time.\n> \n> See https://tests.reproducible-builds.org/rb-pkg/unstable/amd64/unburden-home-dir.html and\n> https://tests.reproducible-builds.org/dbd/unstable/amd64/unburden-home-dir_0.4.diffoscope.html\n> \n> Other cases of this issue are the source package djangorestframework (see\n> https://tests.reproducible-builds.org/rb-pkg/testing/i386/djangorestframework.html and\n> https://tests.reproducible-builds.org/dbd/testing/i386/djangorestframework_3.3.2-2.diffoscope.html)\n> and of course the source package python-mkdocs itself, see\n> https://tests.reproducible-builds.org/rb-pkg/unstable/amd64/python-mkdocs.html and\n> https://tests.reproducible-builds.org/dbd/unstable/amd64/python-mkdocs_0.15.3-3.diffoscope.html\n> \n> At https://reproducible-builds.org/specs/source-date-epoch/ you can find\n> a bound to be widely adopted specification for how to solve such kind of\n> issues generally: If the environment variable SOURCE_DATE_EPOCH exists\n> use the Unix time stamp (seconds since 1st of January 1970, 00:00:00\n> UTC) it contains to preseed all build time stamps with that time stamp\n> instead of the current time.\n> \n> See https://wiki.debian.org/ReproducibleBuilds/TimestampsProposal#Examples\n> for some examples on how to implement support for SOURCE_DATE_EPOCH,\n> including an example for Python.\n\n", "before_files": [{"content": "# coding: utf-8\n\n\"\"\"\nDeals with generating the site-wide navigation.\n\nThis consists of building a set of interlinked page and header objects.\n\"\"\"\n\nfrom __future__ import unicode_literals\nimport datetime\nimport logging\nimport os\n\nfrom mkdocs import utils, exceptions\n\nlog = logging.getLogger(__name__)\n\n\ndef filename_to_title(filename):\n    \"\"\"\n    Automatically generate a default title, given a filename.\n    \"\"\"\n    if utils.is_homepage(filename):\n        return 'Home'\n\n    return utils.filename_to_title(filename)\n\n\nclass SiteNavigation(object):\n    def __init__(self, pages_config, use_directory_urls=True):\n        self.url_context = URLContext()\n        self.file_context = FileContext()\n        self.nav_items, self.pages = _generate_site_navigation(\n            pages_config, self.url_context, use_directory_urls)\n        self.homepage = self.pages[0] if self.pages else None\n        self.use_directory_urls = use_directory_urls\n\n    def __str__(self):\n        return ''.join([str(item) for item in self])\n\n    def __iter__(self):\n        return iter(self.nav_items)\n\n    def walk_pages(self):\n        \"\"\"\n        Returns each page in the site in turn.\n\n        Additionally this sets the active status of the pages and headers,\n        in the site navigation, so that the rendered navbar can correctly\n        highlight the currently active page and/or header item.\n        \"\"\"\n        page = self.homepage\n        page.set_active()\n        self.url_context.set_current_url(page.abs_url)\n        self.file_context.set_current_path(page.input_path)\n        yield page\n        while page.next_page:\n            page.set_active(False)\n            page = page.next_page\n            page.set_active()\n            self.url_context.set_current_url(page.abs_url)\n            self.file_context.set_current_path(page.input_path)\n            yield page\n        page.set_active(False)\n\n    @property\n    def source_files(self):\n        if not hasattr(self, '_source_files'):\n            self._source_files = set([page.input_path for page in self.pages])\n        return self._source_files\n\n\nclass URLContext(object):\n    \"\"\"\n    The URLContext is used to ensure that we can generate the appropriate\n    relative URLs to other pages from any given page in the site.\n\n    We use relative URLs so that static sites can be deployed to any location\n    without having to specify what the path component on the host will be\n    if the documentation is not hosted at the root path.\n    \"\"\"\n\n    def __init__(self):\n        self.base_path = '/'\n\n    def set_current_url(self, current_url):\n        self.base_path = os.path.dirname(current_url)\n\n    def make_relative(self, url):\n        \"\"\"\n        Given a URL path return it as a relative URL,\n        given the context of the current page.\n        \"\"\"\n        suffix = '/' if (url.endswith('/') and len(url) > 1) else ''\n        # Workaround for bug on `os.path.relpath()` in Python 2.6\n        if self.base_path == '/':\n            if url == '/':\n                # Workaround for static assets\n                return '.'\n            return url.lstrip('/')\n        # Under Python 2.6, relative_path adds an extra '/' at the end.\n        relative_path = os.path.relpath(url, start=self.base_path)\n        relative_path = relative_path.rstrip('/') + suffix\n\n        return utils.path_to_url(relative_path)\n\n\nclass FileContext(object):\n    \"\"\"\n    The FileContext is used to ensure that we can generate the appropriate\n    full path for other pages given their relative path from a particular page.\n\n    This is used when we have relative hyperlinks in the documentation, so that\n    we can ensure that they point to markdown documents that actually exist\n    in the `pages` config.\n    \"\"\"\n    def __init__(self):\n        self.current_file = None\n        self.base_path = ''\n\n    def set_current_path(self, current_path):\n        self.current_file = current_path\n        self.base_path = os.path.dirname(current_path)\n\n    def make_absolute(self, path):\n        \"\"\"\n        Given a relative file path return it as a POSIX-style\n        absolute filepath, given the context of the current page.\n        \"\"\"\n        return os.path.normpath(os.path.join(self.base_path, path))\n\n\nclass Page(object):\n    def __init__(self, title, url, path, url_context):\n\n        self.title = title\n        self.abs_url = url\n        self.active = False\n        self.url_context = url_context\n        self.update_date = datetime.datetime.now().strftime(\"%Y-%m-%d\")\n\n        # Relative paths to the input markdown file and output html file.\n        self.input_path = path\n        self.output_path = utils.get_html_path(path)\n\n        # Links to related pages\n        self.previous_page = None\n        self.next_page = None\n        self.ancestors = []\n\n        # Placeholders to be filled in later in the build\n        # process when we have access to the config.\n        self.canonical_url = None\n        self.edit_url = None\n        self.content = None\n        self.meta = None\n        self.toc = None\n\n    @property\n    def url(self):\n        return self.url_context.make_relative(self.abs_url)\n\n    @property\n    def is_homepage(self):\n        return utils.is_homepage(self.input_path)\n\n    @property\n    def is_top_level(self):\n        return len(self.ancestors) == 0\n\n    def __str__(self):\n        return self.indent_print()\n\n    def indent_print(self, depth=0):\n        indent = '    ' * depth\n        active_marker = ' [*]' if self.active else ''\n        title = self.title if (self.title is not None) else '[blank]'\n        return '%s%s - %s%s\\n' % (indent, title, self.abs_url, active_marker)\n\n    def set_active(self, active=True):\n        self.active = active\n        for ancestor in self.ancestors:\n            ancestor.set_active(active)\n\n    def set_canonical_url(self, base):\n        if not base.endswith('/'):\n            base += '/'\n        self.canonical_url = utils.urljoin(base, self.abs_url.lstrip('/'))\n\n    def set_edit_url(self, repo_url, edit_uri):\n        if not repo_url.endswith('/'):\n            repo_url += '/'\n        if not edit_uri:\n            self.edit_url = repo_url\n        else:\n            if not edit_uri.endswith('/'):\n                edit_uri += '/'\n            self.edit_url = utils.urljoin(\n                repo_url + edit_uri,\n                self.input_path)\n\n\nclass Header(object):\n    def __init__(self, title, children):\n        self.title, self.children = title, children\n        self.active = False\n        self.ancestors = []\n\n    def __str__(self):\n        return self.indent_print()\n\n    @property\n    def is_top_level(self):\n        return len(self.ancestors) == 0\n\n    def indent_print(self, depth=0):\n        indent = '    ' * depth\n        active_marker = ' [*]' if self.active else ''\n        ret = '%s%s%s\\n' % (indent, self.title, active_marker)\n        for item in self.children:\n            ret += item.indent_print(depth + 1)\n        return ret\n\n    def set_active(self, active=True):\n        self.active = active\n        for ancestor in self.ancestors:\n            ancestor.set_active(active)\n\n\ndef _path_to_page(path, title, url_context, use_directory_urls):\n    if title is None:\n        title = filename_to_title(path.split(os.path.sep)[-1])\n    url = utils.get_url_path(path, use_directory_urls)\n    return Page(title=title, url=url, path=path,\n                url_context=url_context)\n\n\ndef _follow(config_line, url_context, use_dir_urls, header=None, title=None):\n\n    if isinstance(config_line, utils.string_types):\n        path = os.path.normpath(config_line)\n        page = _path_to_page(path, title, url_context, use_dir_urls)\n\n        if header:\n            page.ancestors = header.ancestors + [header, ]\n            header.children.append(page)\n\n        yield page\n        raise StopIteration\n\n    elif not isinstance(config_line, dict):\n        msg = (\"Line in 'page' config is of type {0}, dict or string \"\n               \"expected. Config: {1}\").format(type(config_line), config_line)\n        raise exceptions.ConfigurationError(msg)\n\n    if len(config_line) > 1:\n        raise exceptions.ConfigurationError(\n            \"Page configs should be in the format 'name: markdown.md'. The \"\n            \"config contains an invalid entry: {0}\".format(config_line))\n    elif len(config_line) == 0:\n        log.warning(\"Ignoring empty line in the pages config.\")\n        raise StopIteration\n\n    next_cat_or_title, subpages_or_path = next(iter(config_line.items()))\n\n    if isinstance(subpages_or_path, utils.string_types):\n        path = subpages_or_path\n        for sub in _follow(path, url_context, use_dir_urls, header=header, title=next_cat_or_title):\n            yield sub\n        raise StopIteration\n\n    elif not isinstance(subpages_or_path, list):\n        msg = (\"Line in 'page' config is of type {0}, list or string \"\n               \"expected for sub pages. Config: {1}\"\n               ).format(type(config_line), config_line)\n        raise exceptions.ConfigurationError(msg)\n\n    next_header = Header(title=next_cat_or_title, children=[])\n    if header:\n        next_header.ancestors = [header]\n        header.children.append(next_header)\n    yield next_header\n\n    subpages = subpages_or_path\n\n    for subpage in subpages:\n        for sub in _follow(subpage, url_context, use_dir_urls, next_header):\n            yield sub\n\n\ndef _generate_site_navigation(pages_config, url_context, use_dir_urls=True):\n    \"\"\"\n    Returns a list of Page and Header instances that represent the\n    top level site navigation.\n    \"\"\"\n    nav_items = []\n    pages = []\n\n    previous = None\n\n    for config_line in pages_config:\n\n        for page_or_header in _follow(\n                config_line, url_context, use_dir_urls):\n\n            if isinstance(page_or_header, Header):\n\n                if page_or_header.is_top_level:\n                    nav_items.append(page_or_header)\n\n            elif isinstance(page_or_header, Page):\n\n                if page_or_header.is_top_level:\n                    nav_items.append(page_or_header)\n\n                pages.append(page_or_header)\n\n                if previous:\n                    page_or_header.previous_page = previous\n                    previous.next_page = page_or_header\n                previous = page_or_header\n\n    if len(pages) == 0:\n        raise exceptions.ConfigurationError(\n            \"No pages found in the pages config. \"\n            \"Remove it entirely to enable automatic page discovery.\")\n\n    return (nav_items, pages)\n", "path": "mkdocs/nav.py"}], "after_files": [{"content": "# coding: utf-8\n\n\"\"\"\nDeals with generating the site-wide navigation.\n\nThis consists of building a set of interlinked page and header objects.\n\"\"\"\n\nfrom __future__ import unicode_literals\nimport datetime\nimport logging\nimport os\n\nfrom mkdocs import utils, exceptions\n\nlog = logging.getLogger(__name__)\n\n\ndef filename_to_title(filename):\n    \"\"\"\n    Automatically generate a default title, given a filename.\n    \"\"\"\n    if utils.is_homepage(filename):\n        return 'Home'\n\n    return utils.filename_to_title(filename)\n\n\nclass SiteNavigation(object):\n    def __init__(self, pages_config, use_directory_urls=True):\n        self.url_context = URLContext()\n        self.file_context = FileContext()\n        self.nav_items, self.pages = _generate_site_navigation(\n            pages_config, self.url_context, use_directory_urls)\n        self.homepage = self.pages[0] if self.pages else None\n        self.use_directory_urls = use_directory_urls\n\n    def __str__(self):\n        return ''.join([str(item) for item in self])\n\n    def __iter__(self):\n        return iter(self.nav_items)\n\n    def walk_pages(self):\n        \"\"\"\n        Returns each page in the site in turn.\n\n        Additionally this sets the active status of the pages and headers,\n        in the site navigation, so that the rendered navbar can correctly\n        highlight the currently active page and/or header item.\n        \"\"\"\n        page = self.homepage\n        page.set_active()\n        self.url_context.set_current_url(page.abs_url)\n        self.file_context.set_current_path(page.input_path)\n        yield page\n        while page.next_page:\n            page.set_active(False)\n            page = page.next_page\n            page.set_active()\n            self.url_context.set_current_url(page.abs_url)\n            self.file_context.set_current_path(page.input_path)\n            yield page\n        page.set_active(False)\n\n    @property\n    def source_files(self):\n        if not hasattr(self, '_source_files'):\n            self._source_files = set([page.input_path for page in self.pages])\n        return self._source_files\n\n\nclass URLContext(object):\n    \"\"\"\n    The URLContext is used to ensure that we can generate the appropriate\n    relative URLs to other pages from any given page in the site.\n\n    We use relative URLs so that static sites can be deployed to any location\n    without having to specify what the path component on the host will be\n    if the documentation is not hosted at the root path.\n    \"\"\"\n\n    def __init__(self):\n        self.base_path = '/'\n\n    def set_current_url(self, current_url):\n        self.base_path = os.path.dirname(current_url)\n\n    def make_relative(self, url):\n        \"\"\"\n        Given a URL path return it as a relative URL,\n        given the context of the current page.\n        \"\"\"\n        suffix = '/' if (url.endswith('/') and len(url) > 1) else ''\n        # Workaround for bug on `os.path.relpath()` in Python 2.6\n        if self.base_path == '/':\n            if url == '/':\n                # Workaround for static assets\n                return '.'\n            return url.lstrip('/')\n        # Under Python 2.6, relative_path adds an extra '/' at the end.\n        relative_path = os.path.relpath(url, start=self.base_path)\n        relative_path = relative_path.rstrip('/') + suffix\n\n        return utils.path_to_url(relative_path)\n\n\nclass FileContext(object):\n    \"\"\"\n    The FileContext is used to ensure that we can generate the appropriate\n    full path for other pages given their relative path from a particular page.\n\n    This is used when we have relative hyperlinks in the documentation, so that\n    we can ensure that they point to markdown documents that actually exist\n    in the `pages` config.\n    \"\"\"\n    def __init__(self):\n        self.current_file = None\n        self.base_path = ''\n\n    def set_current_path(self, current_path):\n        self.current_file = current_path\n        self.base_path = os.path.dirname(current_path)\n\n    def make_absolute(self, path):\n        \"\"\"\n        Given a relative file path return it as a POSIX-style\n        absolute filepath, given the context of the current page.\n        \"\"\"\n        return os.path.normpath(os.path.join(self.base_path, path))\n\n\nclass Page(object):\n    def __init__(self, title, url, path, url_context):\n\n        self.title = title\n        self.abs_url = url\n        self.active = False\n        self.url_context = url_context\n\n        # Support SOURCE_DATE_EPOCH environment variable for \"reproducible\" builds.\n        # See https://reproducible-builds.org/specs/source-date-epoch/\n        if 'SOURCE_DATE_EPOCH' in os.environ:\n            self.update_date = datetime.datetime.utcfromtimestamp(\n                int(os.environ['SOURCE_DATE_EPOCH'])\n            ).strftime(\"%Y-%m-%d\")\n        else:\n            self.update_date = datetime.datetime.now().strftime(\"%Y-%m-%d\")\n\n        # Relative paths to the input markdown file and output html file.\n        self.input_path = path\n        self.output_path = utils.get_html_path(path)\n\n        # Links to related pages\n        self.previous_page = None\n        self.next_page = None\n        self.ancestors = []\n\n        # Placeholders to be filled in later in the build\n        # process when we have access to the config.\n        self.canonical_url = None\n        self.edit_url = None\n        self.content = None\n        self.meta = None\n        self.toc = None\n\n    @property\n    def url(self):\n        return self.url_context.make_relative(self.abs_url)\n\n    @property\n    def is_homepage(self):\n        return utils.is_homepage(self.input_path)\n\n    @property\n    def is_top_level(self):\n        return len(self.ancestors) == 0\n\n    def __str__(self):\n        return self.indent_print()\n\n    def indent_print(self, depth=0):\n        indent = '    ' * depth\n        active_marker = ' [*]' if self.active else ''\n        title = self.title if (self.title is not None) else '[blank]'\n        return '%s%s - %s%s\\n' % (indent, title, self.abs_url, active_marker)\n\n    def set_active(self, active=True):\n        self.active = active\n        for ancestor in self.ancestors:\n            ancestor.set_active(active)\n\n    def set_canonical_url(self, base):\n        if not base.endswith('/'):\n            base += '/'\n        self.canonical_url = utils.urljoin(base, self.abs_url.lstrip('/'))\n\n    def set_edit_url(self, repo_url, edit_uri):\n        if not repo_url.endswith('/'):\n            repo_url += '/'\n        if not edit_uri:\n            self.edit_url = repo_url\n        else:\n            if not edit_uri.endswith('/'):\n                edit_uri += '/'\n            self.edit_url = utils.urljoin(\n                repo_url + edit_uri,\n                self.input_path)\n\n\nclass Header(object):\n    def __init__(self, title, children):\n        self.title, self.children = title, children\n        self.active = False\n        self.ancestors = []\n\n    def __str__(self):\n        return self.indent_print()\n\n    @property\n    def is_top_level(self):\n        return len(self.ancestors) == 0\n\n    def indent_print(self, depth=0):\n        indent = '    ' * depth\n        active_marker = ' [*]' if self.active else ''\n        ret = '%s%s%s\\n' % (indent, self.title, active_marker)\n        for item in self.children:\n            ret += item.indent_print(depth + 1)\n        return ret\n\n    def set_active(self, active=True):\n        self.active = active\n        for ancestor in self.ancestors:\n            ancestor.set_active(active)\n\n\ndef _path_to_page(path, title, url_context, use_directory_urls):\n    if title is None:\n        title = filename_to_title(path.split(os.path.sep)[-1])\n    url = utils.get_url_path(path, use_directory_urls)\n    return Page(title=title, url=url, path=path,\n                url_context=url_context)\n\n\ndef _follow(config_line, url_context, use_dir_urls, header=None, title=None):\n\n    if isinstance(config_line, utils.string_types):\n        path = os.path.normpath(config_line)\n        page = _path_to_page(path, title, url_context, use_dir_urls)\n\n        if header:\n            page.ancestors = header.ancestors + [header, ]\n            header.children.append(page)\n\n        yield page\n        raise StopIteration\n\n    elif not isinstance(config_line, dict):\n        msg = (\"Line in 'page' config is of type {0}, dict or string \"\n               \"expected. Config: {1}\").format(type(config_line), config_line)\n        raise exceptions.ConfigurationError(msg)\n\n    if len(config_line) > 1:\n        raise exceptions.ConfigurationError(\n            \"Page configs should be in the format 'name: markdown.md'. The \"\n            \"config contains an invalid entry: {0}\".format(config_line))\n    elif len(config_line) == 0:\n        log.warning(\"Ignoring empty line in the pages config.\")\n        raise StopIteration\n\n    next_cat_or_title, subpages_or_path = next(iter(config_line.items()))\n\n    if isinstance(subpages_or_path, utils.string_types):\n        path = subpages_or_path\n        for sub in _follow(path, url_context, use_dir_urls, header=header, title=next_cat_or_title):\n            yield sub\n        raise StopIteration\n\n    elif not isinstance(subpages_or_path, list):\n        msg = (\"Line in 'page' config is of type {0}, list or string \"\n               \"expected for sub pages. Config: {1}\"\n               ).format(type(config_line), config_line)\n        raise exceptions.ConfigurationError(msg)\n\n    next_header = Header(title=next_cat_or_title, children=[])\n    if header:\n        next_header.ancestors = [header]\n        header.children.append(next_header)\n    yield next_header\n\n    subpages = subpages_or_path\n\n    for subpage in subpages:\n        for sub in _follow(subpage, url_context, use_dir_urls, next_header):\n            yield sub\n\n\ndef _generate_site_navigation(pages_config, url_context, use_dir_urls=True):\n    \"\"\"\n    Returns a list of Page and Header instances that represent the\n    top level site navigation.\n    \"\"\"\n    nav_items = []\n    pages = []\n\n    previous = None\n\n    for config_line in pages_config:\n\n        for page_or_header in _follow(\n                config_line, url_context, use_dir_urls):\n\n            if isinstance(page_or_header, Header):\n\n                if page_or_header.is_top_level:\n                    nav_items.append(page_or_header)\n\n            elif isinstance(page_or_header, Page):\n\n                if page_or_header.is_top_level:\n                    nav_items.append(page_or_header)\n\n                pages.append(page_or_header)\n\n                if previous:\n                    page_or_header.previous_page = previous\n                    previous.next_page = page_or_header\n                previous = page_or_header\n\n    if len(pages) == 0:\n        raise exceptions.ConfigurationError(\n            \"No pages found in the pages config. \"\n            \"Remove it entirely to enable automatic page discovery.\")\n\n    return (nav_items, pages)\n", "path": "mkdocs/nav.py"}]}