We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
migrate remaining rst docs to myst
We still have several doc pages in .rst format, while most are .md.

@consideRatio proposed migrating to myst, which I agree we should do, and it may be good to get done before the docs-reorg internship gets underway in a few weeks.

The challenge is the following open PRs (mostly from the outreachy application period last month) touch these rst files:

- [x] #4209:
  - docs/source/contributing/setup.rst
- [x] #4186:
  - docs/source/getting-started/security-basics.rst
- [x] #4127:
  - docs/source/contributing/docs.rst
- [x] #4119:
  - docs/source/contributing/tests.rst
- [x] #4094:
  - docs/source/admin/upgrading.rst
  - docs/source/getting-started/security-basics.rst
- [x] #4084:
  - docs/source/index.rst
- [ ] #4066:
  - docs/source/contributing/index.rst
  - docs/source/contributing/layout.rst
- [x] #4008:
  - docs/source/index-admin.rst
- [x] #3053:
  - docs/source/reference/index.rst
- [ ] #2726:
  - docs/source/getting-started/index.rst
  - docs/source/index.rst

<details>
<summary>PR list generated with</summary>

```python
from github import Github as GitHub

gh = GitHub()

repo = gh.get_repo("jupyterhub/jupyterhub")
for pull in repo.get_pulls(state="open"):
    pull_files = [f.filename for f in list(pull.get_files())]
    if any(f.endswith(".rst") for f in pull_files):
        print(f"- [ ] #{pull.number}:")
        for f in pull_files:
            if f.endswith(".rst"):
                print(f"  - {f}")
        continue
```

</details>

We should merge/close as many of these as we can to avoid conflicts (some that just touch an index are no big deal to resolve conflicts).




--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `docs/generate-metrics.py`
Content:
```
1 import os
2 
3 from pytablewriter import RstSimpleTableWriter
4 from pytablewriter.style import Style
5 
6 import jupyterhub.metrics
7 
8 HERE = os.path.abspath(os.path.dirname(__file__))
9 
10 
11 class Generator:
12     @classmethod
13     def create_writer(cls, table_name, headers, values):
14         writer = RstSimpleTableWriter()
15         writer.table_name = table_name
16         writer.headers = headers
17         writer.value_matrix = values
18         writer.margin = 1
19         [writer.set_style(header, Style(align="center")) for header in headers]
20         return writer
21 
22     def _parse_metrics(self):
23         table_rows = []
24         for name in dir(jupyterhub.metrics):
25             obj = getattr(jupyterhub.metrics, name)
26             if obj.__class__.__module__.startswith('prometheus_client.'):
27                 for metric in obj.describe():
28                     table_rows.append([metric.type, metric.name, metric.documentation])
29         return table_rows
30 
31     def prometheus_metrics(self):
32         generated_directory = f"{HERE}/source/reference"
33         if not os.path.exists(generated_directory):
34             os.makedirs(generated_directory)
35 
36         filename = f"{generated_directory}/metrics.rst"
37         table_name = ""
38         headers = ["Type", "Name", "Description"]
39         values = self._parse_metrics()
40         writer = self.create_writer(table_name, headers, values)
41 
42         title = "List of Prometheus Metrics"
43         underline = "============================"
44         content = f"{title}\n{underline}\n{writer.dumps()}"
45         with open(filename, 'w') as f:
46             f.write(content)
47         print(f"Generated {filename}.")
48 
49 
50 def main():
51     doc_generator = Generator()
52     doc_generator.prometheus_metrics()
53 
54 
55 if __name__ == "__main__":
56     main()
57
```

Path: `docs/source/conf.py`
Content:
```
1 # Configuration file for Sphinx to build our documentation to HTML.
2 #
3 # Configuration reference: https://www.sphinx-doc.org/en/master/usage/configuration.html
4 #
5 import contextlib
6 import datetime
7 import io
8 import os
9 import subprocess
10 
11 from docutils import nodes
12 from sphinx.directives.other import SphinxDirective
13 
14 import jupyterhub
15 from jupyterhub.app import JupyterHub
16 
17 # -- Project information -----------------------------------------------------
18 # ref: https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
19 #
20 project = "JupyterHub"
21 author = "Project Jupyter Contributors"
22 copyright = f"{datetime.date.today().year}, {author}"
23 version = "%i.%i" % jupyterhub.version_info[:2]
24 release = jupyterhub.__version__
25 
26 
27 # -- General Sphinx configuration --------------------------------------------
28 # ref: https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
29 #
30 extensions = [
31     "sphinx.ext.autodoc",
32     "sphinx.ext.intersphinx",
33     "sphinx.ext.napoleon",
34     "autodoc_traits",
35     "sphinx_copybutton",
36     "sphinx-jsonschema",
37     "sphinxext.opengraph",
38     "sphinxext.rediraffe",
39     "myst_parser",
40 ]
41 root_doc = "index"
42 source_suffix = [".md", ".rst"]
43 # default_role let's use use `foo` instead of ``foo`` in rST
44 default_role = "literal"
45 
46 
47 # -- MyST configuration ------------------------------------------------------
48 # ref: https://myst-parser.readthedocs.io/en/latest/configuration.html
49 #
50 myst_heading_anchors = 2
51 myst_enable_extensions = [
52     "colon_fence",
53     "deflist",
54 ]
55 
56 
57 # -- Custom directives to generate documentation -----------------------------
58 # ref: https://myst-parser.readthedocs.io/en/latest/syntax/roles-and-directives.html
59 #
60 # We define custom directives to help us generate documentation using Python on
61 # demand when referenced from our documentation files.
62 #
63 
64 # Create a temp instance of JupyterHub for use by two separate directive classes
65 # to get the output from using the "--generate-config" and "--help-all" CLI
66 # flags respectively.
67 #
68 jupyterhub_app = JupyterHub()
69 
70 
71 class ConfigDirective(SphinxDirective):
72     """Generate the configuration file output for use in the documentation."""
73 
74     has_content = False
75     required_arguments = 0
76     optional_arguments = 0
77     final_argument_whitespace = False
78     option_spec = {}
79 
80     def run(self):
81         # The generated configuration file for this version
82         generated_config = jupyterhub_app.generate_config_file()
83         # post-process output
84         home_dir = os.environ["HOME"]
85         generated_config = generated_config.replace(home_dir, "$HOME", 1)
86         par = nodes.literal_block(text=generated_config)
87         return [par]
88 
89 
90 class HelpAllDirective(SphinxDirective):
91     """Print the output of jupyterhub help --all for use in the documentation."""
92 
93     has_content = False
94     required_arguments = 0
95     optional_arguments = 0
96     final_argument_whitespace = False
97     option_spec = {}
98 
99     def run(self):
100         # The output of the help command for this version
101         buffer = io.StringIO()
102         with contextlib.redirect_stdout(buffer):
103             jupyterhub_app.print_help("--help-all")
104         all_help = buffer.getvalue()
105         # post-process output
106         home_dir = os.environ["HOME"]
107         all_help = all_help.replace(home_dir, "$HOME", 1)
108         par = nodes.literal_block(text=all_help)
109         return [par]
110 
111 
112 def setup(app):
113     app.add_css_file("custom.css")
114     app.add_directive("jupyterhub-generate-config", ConfigDirective)
115     app.add_directive("jupyterhub-help-all", HelpAllDirective)
116 
117 
118 # -- Read The Docs -----------------------------------------------------------
119 #
120 # Since RTD runs sphinx-build directly without running "make html", we run the
121 # pre-requisite steps for "make html" from here if needed.
122 #
123 if os.environ.get("READTHEDOCS"):
124     docs = os.path.dirname(os.path.dirname(__file__))
125     subprocess.check_call(["make", "metrics", "scopes"], cwd=docs)
126 
127 
128 # -- Spell checking ----------------------------------------------------------
129 # ref: https://sphinxcontrib-spelling.readthedocs.io/en/latest/customize.html#configuration-options
130 #
131 # The "sphinxcontrib.spelling" extension is optionally enabled if its available.
132 #
133 try:
134     import sphinxcontrib.spelling  # noqa
135 except ImportError:
136     pass
137 else:
138     extensions.append("sphinxcontrib.spelling")
139 spelling_word_list_filename = "spelling_wordlist.txt"
140 
141 
142 # -- Options for HTML output -------------------------------------------------
143 # ref: https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
144 #
145 html_logo = "_static/images/logo/logo.png"
146 html_favicon = "_static/images/logo/favicon.ico"
147 html_static_path = ["_static"]
148 
149 html_theme = "pydata_sphinx_theme"
150 html_theme_options = {
151     "icon_links": [
152         {
153             "name": "GitHub",
154             "url": "https://github.com/jupyterhub/jupyterhub",
155             "icon": "fab fa-github-square",
156         },
157         {
158             "name": "Discourse",
159             "url": "https://discourse.jupyter.org/c/jupyterhub/10",
160             "icon": "fab fa-discourse",
161         },
162     ],
163     "use_edit_page_button": True,
164     "navbar_align": "left",
165 }
166 html_context = {
167     "github_user": "jupyterhub",
168     "github_repo": "jupyterhub",
169     "github_version": "main",
170     "doc_path": "docs/source",
171 }
172 
173 
174 # -- Options for linkcheck builder -------------------------------------------
175 # ref: https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-the-linkcheck-builder
176 #
177 linkcheck_ignore = [
178     r"(.*)github\.com(.*)#",  # javascript based anchors
179     r"(.*)/#%21(.*)/(.*)",  # /#!forum/jupyter - encoded anchor edge case
180     r"https://github.com/[^/]*$",  # too many github usernames / searches in changelog
181     "https://github.com/jupyterhub/jupyterhub/pull/",  # too many PRs in changelog
182     "https://github.com/jupyterhub/jupyterhub/compare/",  # too many comparisons in changelog
183 ]
184 linkcheck_anchors_ignore = [
185     "/#!",
186     "/#%21",
187 ]
188 
189 
190 # -- Intersphinx -------------------------------------------------------------
191 # ref: https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html#configuration
192 #
193 intersphinx_mapping = {
194     "python": ("https://docs.python.org/3/", None),
195     "tornado": ("https://www.tornadoweb.org/en/stable/", None),
196 }
197 # -- Options for the opengraph extension -------------------------------------
198 # ref: https://github.com/wpilibsuite/sphinxext-opengraph#options
199 #
200 # ogp_site_url is set automatically by RTD
201 ogp_image = "_static/logo.png"
202 ogp_use_first_image = True
203 
204 
205 # -- Options for the rediraffe extension -------------------------------------
206 # ref: https://github.com/wpilibsuite/sphinxext-rediraffe#readme
207 #
208 # This extensions help us relocated content without breaking links. If a
209 # document is moved internally, a redirect like should be configured below to
210 # help us not break links.
211 #
212 rediraffe_branch = "main"
213 rediraffe_redirects = {
214     # "old-file": "new-folder/new-file-name",
215 }
216
```
--- 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/docs/generate-metrics.py b/docs/generate-metrics.py
--- a/docs/generate-metrics.py
+++ b/docs/generate-metrics.py
@@ -1,7 +1,6 @@
 import os
 
-from pytablewriter import RstSimpleTableWriter
-from pytablewriter.style import Style
+from pytablewriter import MarkdownTableWriter
 
 import jupyterhub.metrics
 
@@ -11,12 +10,11 @@
 class Generator:
     @classmethod
     def create_writer(cls, table_name, headers, values):
-        writer = RstSimpleTableWriter()
+        writer = MarkdownTableWriter()
         writer.table_name = table_name
         writer.headers = headers
         writer.value_matrix = values
         writer.margin = 1
-        [writer.set_style(header, Style(align="center")) for header in headers]
         return writer
 
     def _parse_metrics(self):
@@ -33,18 +31,17 @@
         if not os.path.exists(generated_directory):
             os.makedirs(generated_directory)
 
-        filename = f"{generated_directory}/metrics.rst"
+        filename = f"{generated_directory}/metrics.md"
         table_name = ""
         headers = ["Type", "Name", "Description"]
         values = self._parse_metrics()
         writer = self.create_writer(table_name, headers, values)
 
-        title = "List of Prometheus Metrics"
-        underline = "============================"
-        content = f"{title}\n{underline}\n{writer.dumps()}"
         with open(filename, 'w') as f:
-            f.write(content)
-        print(f"Generated {filename}.")
+            f.write("# List of Prometheus Metrics\n\n")
+            f.write(writer.dumps())
+            f.write("\n")
+        print(f"Generated {filename}")
 
 
 def main():
diff --git a/docs/source/conf.py b/docs/source/conf.py
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -20,8 +20,6 @@
 project = "JupyterHub"
 author = "Project Jupyter Contributors"
 copyright = f"{datetime.date.today().year}, {author}"
-version = "%i.%i" % jupyterhub.version_info[:2]
-release = jupyterhub.__version__
 
 
 # -- General Sphinx configuration --------------------------------------------
@@ -39,7 +37,7 @@
     "myst_parser",
 ]
 root_doc = "index"
-source_suffix = [".md", ".rst"]
+source_suffix = [".md"]
 # default_role let's use use `foo` instead of ``foo`` in rST
 default_role = "literal"
 
@@ -48,11 +46,21 @@
 # ref: https://myst-parser.readthedocs.io/en/latest/configuration.html
 #
 myst_heading_anchors = 2
+
 myst_enable_extensions = [
+    # available extensions: https://myst-parser.readthedocs.io/en/latest/syntax/optional.html
     "colon_fence",
     "deflist",
+    "fieldlist",
+    "substitution",
 ]
 
+myst_substitutions = {
+    # date example: Dev 07, 2022
+    "date": datetime.date.today().strftime("%b %d, %Y").title(),
+    "version": jupyterhub.__version__,
+}
+
 
 # -- Custom directives to generate documentation -----------------------------
 # ref: https://myst-parser.readthedocs.io/en/latest/syntax/roles-and-directives.html

{"golden_diff": "diff --git a/docs/generate-metrics.py b/docs/generate-metrics.py\n--- a/docs/generate-metrics.py\n+++ b/docs/generate-metrics.py\n@@ -1,7 +1,6 @@\n import os\n \n-from pytablewriter import RstSimpleTableWriter\n-from pytablewriter.style import Style\n+from pytablewriter import MarkdownTableWriter\n \n import jupyterhub.metrics\n \n@@ -11,12 +10,11 @@\n class Generator:\n     @classmethod\n     def create_writer(cls, table_name, headers, values):\n-        writer = RstSimpleTableWriter()\n+        writer = MarkdownTableWriter()\n         writer.table_name = table_name\n         writer.headers = headers\n         writer.value_matrix = values\n         writer.margin = 1\n-        [writer.set_style(header, Style(align=\"center\")) for header in headers]\n         return writer\n \n     def _parse_metrics(self):\n@@ -33,18 +31,17 @@\n         if not os.path.exists(generated_directory):\n             os.makedirs(generated_directory)\n \n-        filename = f\"{generated_directory}/metrics.rst\"\n+        filename = f\"{generated_directory}/metrics.md\"\n         table_name = \"\"\n         headers = [\"Type\", \"Name\", \"Description\"]\n         values = self._parse_metrics()\n         writer = self.create_writer(table_name, headers, values)\n \n-        title = \"List of Prometheus Metrics\"\n-        underline = \"============================\"\n-        content = f\"{title}\\n{underline}\\n{writer.dumps()}\"\n         with open(filename, 'w') as f:\n-            f.write(content)\n-        print(f\"Generated {filename}.\")\n+            f.write(\"# List of Prometheus Metrics\\n\\n\")\n+            f.write(writer.dumps())\n+            f.write(\"\\n\")\n+        print(f\"Generated {filename}\")\n \n \n def main():\ndiff --git a/docs/source/conf.py b/docs/source/conf.py\n--- a/docs/source/conf.py\n+++ b/docs/source/conf.py\n@@ -20,8 +20,6 @@\n project = \"JupyterHub\"\n author = \"Project Jupyter Contributors\"\n copyright = f\"{datetime.date.today().year}, {author}\"\n-version = \"%i.%i\" % jupyterhub.version_info[:2]\n-release = jupyterhub.__version__\n \n \n # -- General Sphinx configuration --------------------------------------------\n@@ -39,7 +37,7 @@\n     \"myst_parser\",\n ]\n root_doc = \"index\"\n-source_suffix = [\".md\", \".rst\"]\n+source_suffix = [\".md\"]\n # default_role let's use use `foo` instead of ``foo`` in rST\n default_role = \"literal\"\n \n@@ -48,11 +46,21 @@\n # ref: https://myst-parser.readthedocs.io/en/latest/configuration.html\n #\n myst_heading_anchors = 2\n+\n myst_enable_extensions = [\n+    # available extensions: https://myst-parser.readthedocs.io/en/latest/syntax/optional.html\n     \"colon_fence\",\n     \"deflist\",\n+    \"fieldlist\",\n+    \"substitution\",\n ]\n \n+myst_substitutions = {\n+    # date example: Dev 07, 2022\n+    \"date\": datetime.date.today().strftime(\"%b %d, %Y\").title(),\n+    \"version\": jupyterhub.__version__,\n+}\n+\n \n # -- Custom directives to generate documentation -----------------------------\n # ref: https://myst-parser.readthedocs.io/en/latest/syntax/roles-and-directives.html\n", "issue": "migrate remaining rst docs to myst\nWe still have several doc pages in .rst format, while most are .md.\r\n\r\n@consideRatio proposed migrating to myst, which I agree we should do, and it may be good to get done before the docs-reorg internship gets underway in a few weeks.\r\n\r\nThe challenge is the following open PRs (mostly from the outreachy application period last month) touch these rst files:\r\n\r\n- [x] #4209:\r\n  - docs/source/contributing/setup.rst\r\n- [x] #4186:\r\n  - docs/source/getting-started/security-basics.rst\r\n- [x] #4127:\r\n  - docs/source/contributing/docs.rst\r\n- [x] #4119:\r\n  - docs/source/contributing/tests.rst\r\n- [x] #4094:\r\n  - docs/source/admin/upgrading.rst\r\n  - docs/source/getting-started/security-basics.rst\r\n- [x] #4084:\r\n  - docs/source/index.rst\r\n- [ ] #4066:\r\n  - docs/source/contributing/index.rst\r\n  - docs/source/contributing/layout.rst\r\n- [x] #4008:\r\n  - docs/source/index-admin.rst\r\n- [x] #3053:\r\n  - docs/source/reference/index.rst\r\n- [ ] #2726:\r\n  - docs/source/getting-started/index.rst\r\n  - docs/source/index.rst\r\n\r\n<details>\r\n<summary>PR list generated with</summary>\r\n\r\n```python\r\nfrom github import Github as GitHub\r\n\r\ngh = GitHub()\r\n\r\nrepo = gh.get_repo(\"jupyterhub/jupyterhub\")\r\nfor pull in repo.get_pulls(state=\"open\"):\r\n    pull_files = [f.filename for f in list(pull.get_files())]\r\n    if any(f.endswith(\".rst\") for f in pull_files):\r\n        print(f\"- [ ] #{pull.number}:\")\r\n        for f in pull_files:\r\n            if f.endswith(\".rst\"):\r\n                print(f\"  - {f}\")\r\n        continue\r\n```\r\n\r\n</details>\r\n\r\nWe should merge/close as many of these as we can to avoid conflicts (some that just touch an index are no big deal to resolve conflicts).\r\n\r\n\r\n\n", "before_files": [{"content": "import os\n\nfrom pytablewriter import RstSimpleTableWriter\nfrom pytablewriter.style import Style\n\nimport jupyterhub.metrics\n\nHERE = os.path.abspath(os.path.dirname(__file__))\n\n\nclass Generator:\n    @classmethod\n    def create_writer(cls, table_name, headers, values):\n        writer = RstSimpleTableWriter()\n        writer.table_name = table_name\n        writer.headers = headers\n        writer.value_matrix = values\n        writer.margin = 1\n        [writer.set_style(header, Style(align=\"center\")) for header in headers]\n        return writer\n\n    def _parse_metrics(self):\n        table_rows = []\n        for name in dir(jupyterhub.metrics):\n            obj = getattr(jupyterhub.metrics, name)\n            if obj.__class__.__module__.startswith('prometheus_client.'):\n                for metric in obj.describe():\n                    table_rows.append([metric.type, metric.name, metric.documentation])\n        return table_rows\n\n    def prometheus_metrics(self):\n        generated_directory = f\"{HERE}/source/reference\"\n        if not os.path.exists(generated_directory):\n            os.makedirs(generated_directory)\n\n        filename = f\"{generated_directory}/metrics.rst\"\n        table_name = \"\"\n        headers = [\"Type\", \"Name\", \"Description\"]\n        values = self._parse_metrics()\n        writer = self.create_writer(table_name, headers, values)\n\n        title = \"List of Prometheus Metrics\"\n        underline = \"============================\"\n        content = f\"{title}\\n{underline}\\n{writer.dumps()}\"\n        with open(filename, 'w') as f:\n            f.write(content)\n        print(f\"Generated {filename}.\")\n\n\ndef main():\n    doc_generator = Generator()\n    doc_generator.prometheus_metrics()\n\n\nif __name__ == \"__main__\":\n    main()\n", "path": "docs/generate-metrics.py"}, {"content": "# Configuration file for Sphinx to build our documentation to HTML.\n#\n# Configuration reference: https://www.sphinx-doc.org/en/master/usage/configuration.html\n#\nimport contextlib\nimport datetime\nimport io\nimport os\nimport subprocess\n\nfrom docutils import nodes\nfrom sphinx.directives.other import SphinxDirective\n\nimport jupyterhub\nfrom jupyterhub.app import JupyterHub\n\n# -- Project information -----------------------------------------------------\n# ref: https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information\n#\nproject = \"JupyterHub\"\nauthor = \"Project Jupyter Contributors\"\ncopyright = f\"{datetime.date.today().year}, {author}\"\nversion = \"%i.%i\" % jupyterhub.version_info[:2]\nrelease = jupyterhub.__version__\n\n\n# -- General Sphinx configuration --------------------------------------------\n# ref: https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration\n#\nextensions = [\n    \"sphinx.ext.autodoc\",\n    \"sphinx.ext.intersphinx\",\n    \"sphinx.ext.napoleon\",\n    \"autodoc_traits\",\n    \"sphinx_copybutton\",\n    \"sphinx-jsonschema\",\n    \"sphinxext.opengraph\",\n    \"sphinxext.rediraffe\",\n    \"myst_parser\",\n]\nroot_doc = \"index\"\nsource_suffix = [\".md\", \".rst\"]\n# default_role let's use use `foo` instead of ``foo`` in rST\ndefault_role = \"literal\"\n\n\n# -- MyST configuration ------------------------------------------------------\n# ref: https://myst-parser.readthedocs.io/en/latest/configuration.html\n#\nmyst_heading_anchors = 2\nmyst_enable_extensions = [\n    \"colon_fence\",\n    \"deflist\",\n]\n\n\n# -- Custom directives to generate documentation -----------------------------\n# ref: https://myst-parser.readthedocs.io/en/latest/syntax/roles-and-directives.html\n#\n# We define custom directives to help us generate documentation using Python on\n# demand when referenced from our documentation files.\n#\n\n# Create a temp instance of JupyterHub for use by two separate directive classes\n# to get the output from using the \"--generate-config\" and \"--help-all\" CLI\n# flags respectively.\n#\njupyterhub_app = JupyterHub()\n\n\nclass ConfigDirective(SphinxDirective):\n    \"\"\"Generate the configuration file output for use in the documentation.\"\"\"\n\n    has_content = False\n    required_arguments = 0\n    optional_arguments = 0\n    final_argument_whitespace = False\n    option_spec = {}\n\n    def run(self):\n        # The generated configuration file for this version\n        generated_config = jupyterhub_app.generate_config_file()\n        # post-process output\n        home_dir = os.environ[\"HOME\"]\n        generated_config = generated_config.replace(home_dir, \"$HOME\", 1)\n        par = nodes.literal_block(text=generated_config)\n        return [par]\n\n\nclass HelpAllDirective(SphinxDirective):\n    \"\"\"Print the output of jupyterhub help --all for use in the documentation.\"\"\"\n\n    has_content = False\n    required_arguments = 0\n    optional_arguments = 0\n    final_argument_whitespace = False\n    option_spec = {}\n\n    def run(self):\n        # The output of the help command for this version\n        buffer = io.StringIO()\n        with contextlib.redirect_stdout(buffer):\n            jupyterhub_app.print_help(\"--help-all\")\n        all_help = buffer.getvalue()\n        # post-process output\n        home_dir = os.environ[\"HOME\"]\n        all_help = all_help.replace(home_dir, \"$HOME\", 1)\n        par = nodes.literal_block(text=all_help)\n        return [par]\n\n\ndef setup(app):\n    app.add_css_file(\"custom.css\")\n    app.add_directive(\"jupyterhub-generate-config\", ConfigDirective)\n    app.add_directive(\"jupyterhub-help-all\", HelpAllDirective)\n\n\n# -- Read The Docs -----------------------------------------------------------\n#\n# Since RTD runs sphinx-build directly without running \"make html\", we run the\n# pre-requisite steps for \"make html\" from here if needed.\n#\nif os.environ.get(\"READTHEDOCS\"):\n    docs = os.path.dirname(os.path.dirname(__file__))\n    subprocess.check_call([\"make\", \"metrics\", \"scopes\"], cwd=docs)\n\n\n# -- Spell checking ----------------------------------------------------------\n# ref: https://sphinxcontrib-spelling.readthedocs.io/en/latest/customize.html#configuration-options\n#\n# The \"sphinxcontrib.spelling\" extension is optionally enabled if its available.\n#\ntry:\n    import sphinxcontrib.spelling  # noqa\nexcept ImportError:\n    pass\nelse:\n    extensions.append(\"sphinxcontrib.spelling\")\nspelling_word_list_filename = \"spelling_wordlist.txt\"\n\n\n# -- Options for HTML output -------------------------------------------------\n# ref: https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output\n#\nhtml_logo = \"_static/images/logo/logo.png\"\nhtml_favicon = \"_static/images/logo/favicon.ico\"\nhtml_static_path = [\"_static\"]\n\nhtml_theme = \"pydata_sphinx_theme\"\nhtml_theme_options = {\n    \"icon_links\": [\n        {\n            \"name\": \"GitHub\",\n            \"url\": \"https://github.com/jupyterhub/jupyterhub\",\n            \"icon\": \"fab fa-github-square\",\n        },\n        {\n            \"name\": \"Discourse\",\n            \"url\": \"https://discourse.jupyter.org/c/jupyterhub/10\",\n            \"icon\": \"fab fa-discourse\",\n        },\n    ],\n    \"use_edit_page_button\": True,\n    \"navbar_align\": \"left\",\n}\nhtml_context = {\n    \"github_user\": \"jupyterhub\",\n    \"github_repo\": \"jupyterhub\",\n    \"github_version\": \"main\",\n    \"doc_path\": \"docs/source\",\n}\n\n\n# -- Options for linkcheck builder -------------------------------------------\n# ref: https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-the-linkcheck-builder\n#\nlinkcheck_ignore = [\n    r\"(.*)github\\.com(.*)#\",  # javascript based anchors\n    r\"(.*)/#%21(.*)/(.*)\",  # /#!forum/jupyter - encoded anchor edge case\n    r\"https://github.com/[^/]*$\",  # too many github usernames / searches in changelog\n    \"https://github.com/jupyterhub/jupyterhub/pull/\",  # too many PRs in changelog\n    \"https://github.com/jupyterhub/jupyterhub/compare/\",  # too many comparisons in changelog\n]\nlinkcheck_anchors_ignore = [\n    \"/#!\",\n    \"/#%21\",\n]\n\n\n# -- Intersphinx -------------------------------------------------------------\n# ref: https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html#configuration\n#\nintersphinx_mapping = {\n    \"python\": (\"https://docs.python.org/3/\", None),\n    \"tornado\": (\"https://www.tornadoweb.org/en/stable/\", None),\n}\n# -- Options for the opengraph extension -------------------------------------\n# ref: https://github.com/wpilibsuite/sphinxext-opengraph#options\n#\n# ogp_site_url is set automatically by RTD\nogp_image = \"_static/logo.png\"\nogp_use_first_image = True\n\n\n# -- Options for the rediraffe extension -------------------------------------\n# ref: https://github.com/wpilibsuite/sphinxext-rediraffe#readme\n#\n# This extensions help us relocated content without breaking links. If a\n# document is moved internally, a redirect like should be configured below to\n# help us not break links.\n#\nrediraffe_branch = \"main\"\nrediraffe_redirects = {\n    # \"old-file\": \"new-folder/new-file-name\",\n}\n", "path": "docs/source/conf.py"}], "after_files": [{"content": "import os\n\nfrom pytablewriter import MarkdownTableWriter\n\nimport jupyterhub.metrics\n\nHERE = os.path.abspath(os.path.dirname(__file__))\n\n\nclass Generator:\n    @classmethod\n    def create_writer(cls, table_name, headers, values):\n        writer = MarkdownTableWriter()\n        writer.table_name = table_name\n        writer.headers = headers\n        writer.value_matrix = values\n        writer.margin = 1\n        return writer\n\n    def _parse_metrics(self):\n        table_rows = []\n        for name in dir(jupyterhub.metrics):\n            obj = getattr(jupyterhub.metrics, name)\n            if obj.__class__.__module__.startswith('prometheus_client.'):\n                for metric in obj.describe():\n                    table_rows.append([metric.type, metric.name, metric.documentation])\n        return table_rows\n\n    def prometheus_metrics(self):\n        generated_directory = f\"{HERE}/source/reference\"\n        if not os.path.exists(generated_directory):\n            os.makedirs(generated_directory)\n\n        filename = f\"{generated_directory}/metrics.md\"\n        table_name = \"\"\n        headers = [\"Type\", \"Name\", \"Description\"]\n        values = self._parse_metrics()\n        writer = self.create_writer(table_name, headers, values)\n\n        with open(filename, 'w') as f:\n            f.write(\"# List of Prometheus Metrics\\n\\n\")\n            f.write(writer.dumps())\n            f.write(\"\\n\")\n        print(f\"Generated {filename}\")\n\n\ndef main():\n    doc_generator = Generator()\n    doc_generator.prometheus_metrics()\n\n\nif __name__ == \"__main__\":\n    main()\n", "path": "docs/generate-metrics.py"}, {"content": "# Configuration file for Sphinx to build our documentation to HTML.\n#\n# Configuration reference: https://www.sphinx-doc.org/en/master/usage/configuration.html\n#\nimport contextlib\nimport datetime\nimport io\nimport os\nimport subprocess\n\nfrom docutils import nodes\nfrom sphinx.directives.other import SphinxDirective\n\nimport jupyterhub\nfrom jupyterhub.app import JupyterHub\n\n# -- Project information -----------------------------------------------------\n# ref: https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information\n#\nproject = \"JupyterHub\"\nauthor = \"Project Jupyter Contributors\"\ncopyright = f\"{datetime.date.today().year}, {author}\"\n\n\n# -- General Sphinx configuration --------------------------------------------\n# ref: https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration\n#\nextensions = [\n    \"sphinx.ext.autodoc\",\n    \"sphinx.ext.intersphinx\",\n    \"sphinx.ext.napoleon\",\n    \"autodoc_traits\",\n    \"sphinx_copybutton\",\n    \"sphinx-jsonschema\",\n    \"sphinxext.opengraph\",\n    \"sphinxext.rediraffe\",\n    \"myst_parser\",\n]\nroot_doc = \"index\"\nsource_suffix = [\".md\"]\n# default_role let's use use `foo` instead of ``foo`` in rST\ndefault_role = \"literal\"\n\n\n# -- MyST configuration ------------------------------------------------------\n# ref: https://myst-parser.readthedocs.io/en/latest/configuration.html\n#\nmyst_heading_anchors = 2\n\nmyst_enable_extensions = [\n    # available extensions: https://myst-parser.readthedocs.io/en/latest/syntax/optional.html\n    \"colon_fence\",\n    \"deflist\",\n    \"fieldlist\",\n    \"substitution\",\n]\n\nmyst_substitutions = {\n    # date example: Dev 07, 2022\n    \"date\": datetime.date.today().strftime(\"%b %d, %Y\").title(),\n    \"version\": jupyterhub.__version__,\n}\n\n\n# -- Custom directives to generate documentation -----------------------------\n# ref: https://myst-parser.readthedocs.io/en/latest/syntax/roles-and-directives.html\n#\n# We define custom directives to help us generate documentation using Python on\n# demand when referenced from our documentation files.\n#\n\n# Create a temp instance of JupyterHub for use by two separate directive classes\n# to get the output from using the \"--generate-config\" and \"--help-all\" CLI\n# flags respectively.\n#\njupyterhub_app = JupyterHub()\n\n\nclass ConfigDirective(SphinxDirective):\n    \"\"\"Generate the configuration file output for use in the documentation.\"\"\"\n\n    has_content = False\n    required_arguments = 0\n    optional_arguments = 0\n    final_argument_whitespace = False\n    option_spec = {}\n\n    def run(self):\n        # The generated configuration file for this version\n        generated_config = jupyterhub_app.generate_config_file()\n        # post-process output\n        home_dir = os.environ[\"HOME\"]\n        generated_config = generated_config.replace(home_dir, \"$HOME\", 1)\n        par = nodes.literal_block(text=generated_config)\n        return [par]\n\n\nclass HelpAllDirective(SphinxDirective):\n    \"\"\"Print the output of jupyterhub help --all for use in the documentation.\"\"\"\n\n    has_content = False\n    required_arguments = 0\n    optional_arguments = 0\n    final_argument_whitespace = False\n    option_spec = {}\n\n    def run(self):\n        # The output of the help command for this version\n        buffer = io.StringIO()\n        with contextlib.redirect_stdout(buffer):\n            jupyterhub_app.print_help(\"--help-all\")\n        all_help = buffer.getvalue()\n        # post-process output\n        home_dir = os.environ[\"HOME\"]\n        all_help = all_help.replace(home_dir, \"$HOME\", 1)\n        par = nodes.literal_block(text=all_help)\n        return [par]\n\n\ndef setup(app):\n    app.add_css_file(\"custom.css\")\n    app.add_directive(\"jupyterhub-generate-config\", ConfigDirective)\n    app.add_directive(\"jupyterhub-help-all\", HelpAllDirective)\n\n\n# -- Read The Docs -----------------------------------------------------------\n#\n# Since RTD runs sphinx-build directly without running \"make html\", we run the\n# pre-requisite steps for \"make html\" from here if needed.\n#\nif os.environ.get(\"READTHEDOCS\"):\n    docs = os.path.dirname(os.path.dirname(__file__))\n    subprocess.check_call([\"make\", \"metrics\", \"scopes\"], cwd=docs)\n\n\n# -- Spell checking ----------------------------------------------------------\n# ref: https://sphinxcontrib-spelling.readthedocs.io/en/latest/customize.html#configuration-options\n#\n# The \"sphinxcontrib.spelling\" extension is optionally enabled if its available.\n#\ntry:\n    import sphinxcontrib.spelling  # noqa\nexcept ImportError:\n    pass\nelse:\n    extensions.append(\"sphinxcontrib.spelling\")\nspelling_word_list_filename = \"spelling_wordlist.txt\"\n\n\n# -- Options for HTML output -------------------------------------------------\n# ref: https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output\n#\nhtml_logo = \"_static/images/logo/logo.png\"\nhtml_favicon = \"_static/images/logo/favicon.ico\"\nhtml_static_path = [\"_static\"]\n\nhtml_theme = \"pydata_sphinx_theme\"\nhtml_theme_options = {\n    \"icon_links\": [\n        {\n            \"name\": \"GitHub\",\n            \"url\": \"https://github.com/jupyterhub/jupyterhub\",\n            \"icon\": \"fab fa-github-square\",\n        },\n        {\n            \"name\": \"Discourse\",\n            \"url\": \"https://discourse.jupyter.org/c/jupyterhub/10\",\n            \"icon\": \"fab fa-discourse\",\n        },\n    ],\n    \"use_edit_page_button\": True,\n    \"navbar_align\": \"left\",\n}\nhtml_context = {\n    \"github_user\": \"jupyterhub\",\n    \"github_repo\": \"jupyterhub\",\n    \"github_version\": \"main\",\n    \"doc_path\": \"docs/source\",\n}\n\n\n# -- Options for linkcheck builder -------------------------------------------\n# ref: https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-the-linkcheck-builder\n#\nlinkcheck_ignore = [\n    r\"(.*)github\\.com(.*)#\",  # javascript based anchors\n    r\"(.*)/#%21(.*)/(.*)\",  # /#!forum/jupyter - encoded anchor edge case\n    r\"https://github.com/[^/]*$\",  # too many github usernames / searches in changelog\n    \"https://github.com/jupyterhub/jupyterhub/pull/\",  # too many PRs in changelog\n    \"https://github.com/jupyterhub/jupyterhub/compare/\",  # too many comparisons in changelog\n]\nlinkcheck_anchors_ignore = [\n    \"/#!\",\n    \"/#%21\",\n]\n\n\n# -- Intersphinx -------------------------------------------------------------\n# ref: https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html#configuration\n#\nintersphinx_mapping = {\n    \"python\": (\"https://docs.python.org/3/\", None),\n    \"tornado\": (\"https://www.tornadoweb.org/en/stable/\", None),\n}\n# -- Options for the opengraph extension -------------------------------------\n# ref: https://github.com/wpilibsuite/sphinxext-opengraph#options\n#\n# ogp_site_url is set automatically by RTD\nogp_image = \"_static/logo.png\"\nogp_use_first_image = True\n\n\n# -- Options for the rediraffe extension -------------------------------------\n# ref: https://github.com/wpilibsuite/sphinxext-rediraffe#readme\n#\n# This extensions help us relocated content without breaking links. If a\n# document is moved internally, a redirect like should be configured below to\n# help us not break links.\n#\nrediraffe_branch = \"main\"\nrediraffe_redirects = {\n    # \"old-file\": \"new-folder/new-file-name\",\n}\n", "path": "docs/source/conf.py"}]}