mkdocs / mkdocs-click

An MkDocs extension to generate documentation for Click command line applications
https://pypi.org/project/mkdocs-click
Apache License 2.0
109 stars 16 forks source link

mkdocs serve command fails can't find module specified in :module: attribute #46

Open prowler0305 opened 2 years ago

prowler0305 commented 2 years ago

Description

I am getting a ModuleNotFoundError exception thrown when trying to start the mkdocs serve development server and even trying to do mkdocs build.

I'm not sure why it can't find my module called open_bandage as I understand it uses relative path finding and I'm executing the mkdocs serve command from my overall Intellij directory. (i.e. same level as my docs, mkdocs.yml, etc.)

ERROR   -  Error reading page 'index.md': No module named 'open_bandage'
Traceback (most recent call last):
  File "/home/aspea002/miniconda3/envs/open_bandage/bin/mkdocs", line 8, in <module>
    sys.exit(cli())
  File "/home/aspea002/miniconda3/envs/open_bandage/lib/python3.7/site-packages/click/core.py", line 829, in __call__
    return self.main(*args, **kwargs)
  File "/home/aspea002/miniconda3/envs/open_bandage/lib/python3.7/site-packages/click/core.py", line 782, in main
    rv = self.invoke(ctx)
  File "/home/aspea002/miniconda3/envs/open_bandage/lib/python3.7/site-packages/click/core.py", line 1259, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/aspea002/miniconda3/envs/open_bandage/lib/python3.7/site-packages/click/core.py", line 1066, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/aspea002/miniconda3/envs/open_bandage/lib/python3.7/site-packages/click/core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "/home/aspea002/miniconda3/envs/open_bandage/lib/python3.7/site-packages/mkdocs/__main__.py", line 136, in serve_command
    **kwargs
  File "/home/aspea002/miniconda3/envs/open_bandage/lib/python3.7/site-packages/mkdocs/commands/serve.py", line 141, in serve
    config = builder()
  File "/home/aspea002/miniconda3/envs/open_bandage/lib/python3.7/site-packages/mkdocs/commands/serve.py", line 136, in builder
    build(config, live_server=live_server, dirty=dirty)
  File "/home/aspea002/miniconda3/envs/open_bandage/lib/python3.7/site-packages/mkdocs/commands/build.py", line 271, in build
    _populate_page(file.page, config, files, dirty)
  File "/home/aspea002/miniconda3/envs/open_bandage/lib/python3.7/site-packages/mkdocs/commands/build.py", line 171, in _populate_page
    page.render(config, files)
  File "/home/aspea002/miniconda3/envs/open_bandage/lib/python3.7/site-packages/mkdocs/structure/pages.py", line 175, in render
    self.content = md.convert(self.markdown)
  File "/home/aspea002/miniconda3/envs/open_bandage/lib/python3.7/site-packages/markdown/core.py", line 261, in convert
    self.lines = prep.run(self.lines)
  File "/home/aspea002/miniconda3/envs/open_bandage/lib/python3.7/site-packages/mkdocs_click/_extension.py", line 46, in run
    replace=lambda **options: replace_command_docs(has_attr_list=self._has_attr_list, **options),
  File "/home/aspea002/miniconda3/envs/open_bandage/lib/python3.7/site-packages/mkdocs_click/_processing.py", line 35, in replace_blocks
    yield from replace(**options)
  File "/home/aspea002/miniconda3/envs/open_bandage/lib/python3.7/site-packages/mkdocs_click/_extension.py", line 46, in <lambda>
    replace=lambda **options: replace_command_docs(has_attr_list=self._has_attr_list, **options),
  File "/home/aspea002/miniconda3/envs/open_bandage/lib/python3.7/site-packages/mkdocs_click/_extension.py", line 27, in replace_command_docs
    command_obj = load_command(module, command)
  File "/home/aspea002/miniconda3/envs/open_bandage/lib/python3.7/site-packages/mkdocs_click/_loader.py", line 15, in load_command
    command = _load_obj(module, attribute)
  File "/home/aspea002/miniconda3/envs/open_bandage/lib/python3.7/site-packages/mkdocs_click/_loader.py", line 25, in _load_obj
    mod = importlib.import_module(module)
  File "/home/aspea002/miniconda3/envs/open_bandage/lib/python3.7/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
  File "<frozen importlib._bootstrap>", line 983, in _find_and_load
  File "<frozen importlib._bootstrap>", line 953, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
  File "<frozen importlib._bootstrap>", line 983, in _find_and_load
  File "<frozen importlib._bootstrap>", line 953, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
  File "<frozen importlib._bootstrap>", line 983, in _find_and_load
  File "<frozen importlib._bootstrap>", line 965, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'open_bandage'

mkdocs.yml

site_name: Open Bandage
site_author: 'Andrew Spear'
site_url: 'https://xxxxxxxxxxxxxxxxxxxx'
theme:
  name: 'material'
  palette:
    # Light mode
    - media: "(prefers-color-scheme: light)"
      scheme: default
      primary: light green
      accent: deep orange
      toggle:
        icon: material/weather-night
        name: Switch to dark mode
    # Dark Mode
    - media: "(prefers-color-scheme: dark)"
      scheme: slate
      primary: deep orange
      accent: lime
      toggle:
        icon: material/weather-sunny
        name: Switch to light mode
  icon:
    logo: material/bandage
  favicon: images/uscc_logo.png
  features:
    - navigation.tabs
    - navigation.sections
    - navigation.top
#    - toc.integrate
markdown_extensions:
  - admonition
  - pymdownx.highlight:
      linenums: true
  - pymdownx.inlinehilite
  - pymdownx.superfences
  - pymdownx.smartsymbols
  - pymdownx.details
  - pymdownx.caret
  - pymdownx.mark
  - pymdownx.tilde
  - pymdownx.betterem:
      smart_enable: all
  - def_list
  - pymdownx.emoji:
      emoji_index: !!python/name:materialx.emoji.twemoji
      emoji_generator: !!python/name:materialx.emoji.to_svg
  - pymdownx.tabbed
  - attr_list
  - mkdocs-click
  - toc:
      toc_depth: 4
nav:
  - Home: index.md
  - Developer Info: development.md

index.md

::: mkdocs-click
    :module: open_bandage.scripts.cli
    :command: cli
    :prog_name: openbandage
    :depth: 2
    :style: table

Project Directory structure

image

fernandascovino commented 2 years ago

Hi! Same issue here, from project basedosdados:

project structure:

python-package:
  - basedosdados
    - cli
       - cli.py
docs:
  - reference_api_cli.md

reference_api_cli.md

# Linha de comando (CLI)

::: mkdocs-click
    :module: python-package.basedosdados.cli.cli
    :command: dataset

$ mkdocs serve:

ERROR    -  Error reading page 'reference_api_cli.md': No module named 'python-package'
Traceback (most recent call last):
  File "/Users/fernandascovino/Projects/mais/.mais/bin/mkdocs", line 8, in <module>
    sys.exit(cli())
  File "/Users/fernandascovino/Projects/mais/.mais/lib/python3.9/site-packages/click/core.py", line 1128, in __call__
    return self.main(*args, **kwargs)
  File "/Users/fernandascovino/Projects/mais/.mais/lib/python3.9/site-packages/click/core.py", line 1053, in main
    rv = self.invoke(ctx)
  File "/Users/fernandascovino/Projects/mais/.mais/lib/python3.9/site-packages/click/core.py", line 1659, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/Users/fernandascovino/Projects/mais/.mais/lib/python3.9/site-packages/click/core.py", line 1395, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/Users/fernandascovino/Projects/mais/.mais/lib/python3.9/site-packages/click/core.py", line 754, in invoke
    return __callback(*args, **kwargs)
  File "/Users/fernandascovino/Projects/mais/.mais/lib/python3.9/site-packages/mkdocs/__main__.py", line 177, in serve_command
    serve.serve(dev_addr=dev_addr, livereload=livereload, **kwargs)
  File "/Users/fernandascovino/Projects/mais/.mais/lib/python3.9/site-packages/mkdocs/commands/serve.py", line 54, in serve
    config = builder()
  File "/Users/fernandascovino/Projects/mais/.mais/lib/python3.9/site-packages/mkdocs/commands/serve.py", line 49, in builder
    build(config, live_server=live_server, dirty=dirty)
  File "/Users/fernandascovino/Projects/mais/.mais/lib/python3.9/site-packages/mkdocs/commands/build.py", line 292, in build
    _populate_page(file.page, config, files, dirty)
  File "/Users/fernandascovino/Projects/mais/.mais/lib/python3.9/site-packages/mkdocs/commands/build.py", line 174, in _populate_page
    page.render(config, files)
  File "/Users/fernandascovino/Projects/mais/.mais/lib/python3.9/site-packages/mkdocs/structure/pages.py", line 174, in render
    self.content = md.convert(self.markdown)
  File "/Users/fernandascovino/Projects/mais/.mais/lib/python3.9/site-packages/markdown/core.py", line 261, in convert
    self.lines = prep.run(self.lines)
  File "/Users/fernandascovino/Projects/mais/.mais/lib/python3.9/site-packages/mkdocs_click/_extension.py", line 48, in run
    return list(
  File "/Users/fernandascovino/Projects/mais/.mais/lib/python3.9/site-packages/mkdocs_click/_processing.py", line 35, in replace_blocks
    yield from replace(**options)
  File "/Users/fernandascovino/Projects/mais/.mais/lib/python3.9/site-packages/mkdocs_click/_extension.py", line 52, in <lambda>
    replace=lambda **options: replace_command_docs(has_attr_list=self._has_attr_list, **options),
  File "/Users/fernandascovino/Projects/mais/.mais/lib/python3.9/site-packages/mkdocs_click/_extension.py", line 28, in replace_command_docs
    command_obj = load_command(module, command)
  File "/Users/fernandascovino/Projects/mais/.mais/lib/python3.9/site-packages/mkdocs_click/_loader.py", line 15, in load_command
    command = _load_obj(module, attribute)
  File "/Users/fernandascovino/Projects/mais/.mais/lib/python3.9/site-packages/mkdocs_click/_loader.py", line 25, in _load_obj
    mod = importlib.import_module(module)
  File "/usr/local/Cellar/python@3.9/3.9.7_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 972, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 972, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 972, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 984, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'python-package'

Additionally, when I tried https://github.com/DataDog/mkdocs-click/pull/37 solution (change from mkdocs-click to mkdocs_click) it doesn't give any error but also nothing shows up.

image

ofek commented 2 years ago

Hello! Have you both installed your package into the same environment as mkdocs?

NBStephens commented 2 years ago

I have the same error and I can't even get the example to run properly with fresh environments containing only the needed libraries.

I have tried it on Windows, and Ubuntu with Python 3.8 and 3.9.

Some strange behavior occurs if I paste in the examples to new Scripts I will get a page with no information beyond the Markdown references:

INFO     -  Building documentation...
INFO     -  [10:54:08] Reloading browsers
INFO     -  [10:54:08] Browser connected: http://127.0.0.1:8000/cli/
INFO     -  [10:54:08] Browser connected: http://127.0.0.1:8000/cli/
INFO     -  [10:54:08] Browser connected: http://127.0.0.1:8000/cli/
INFO     -  [10:55:38] Browser connected: http://127.0.0.1:8000/cli/
WARNING  -  [10:55:40] "GET / HTTP/1.1" code 404
INFO     -  [10:55:41] Browser connected: http://127.0.0.1:8000/cli/

But, if I hit return and save the cli.md it will crash and give the No module named 'app' error:

    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
  File "<frozen importlib._bootstrap>", line 991, in _find_and_load
  File "<frozen importlib._bootstrap>", line 961, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
  File "<frozen importlib._bootstrap>", line 991, in _find_and_load
  File "<frozen importlib._bootstrap>", line 973, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'app'
fernandascovino commented 2 years ago

SOLVED! 🎉

Tricky one. I was passing the group name as the command value, so it didn't recognized it.

After changing it to the [name].command() it worked.

To illustrate:

cli.py

@click.group(name="dataset") ==> I was using "dataset" on :command: 
@click.pass_context
def cli_dataset(ctx):
    pass

@cli_dataset.command(name="init", help="...") ==> But I actually needed to pass "cli_dataset"
@click.argument("dataset_id")
@click.option(
    "--replace",
    is_flag=True,
    help="Whether to replace current metadata files",
)

.md

# Linha de comando (CLI)

::: mkdocs_click
    :module: basedosdados.cli.cli ==> I also removed the `python-package`path here and put it on the .yml
    :command: cli_dataset
    :depth: 1

PS: I also changed mkdocs-click to mkdocs_click on mkdocs.yml and requirements.txt

All the changes made are here: https://github.com/basedosdados/mais/pull/1134/files

miketheartguy commented 2 years ago

I was able to resolve only by setting PYTHONPATH: PYTHONPATH=$PWD mkdocs build

Without setting it, I'll get ModuleNotFoundError: No module named 'app'

I've been able to replicate on a new python project:

.
├── app.py
├── docs
│   └── index.md
└── mkdocs.yml

mkdocs.yml:

site_name: My Docs

markdown_extensions:
    - mkdocs-click

app.py

import click

@click.group()
def cli():
    """Main entrypoint."""
    pass

@cli.command()
@click.option("-d", "--debug", help="Include debug output.")
def build(debug):
    """Build production assets."""
    pass

docs\index.md

::: mkdocs-click
    :module: app
    :command: cli
jackblk commented 1 year ago

Setting PYTHONPATH helps, but if I don't use relative import then it will cause ModuleNotFoundError as well.

For example:

Structure

.
├── app.py
├── config.py
│   └── docs
│       └── index.md
└── mkdocs.yml

app.py

import click
from config import Config

@click.group()
def cli():
    """Main entrypoint."""
    Config.get_env()
    pass

@cli.command()
@click.option("-d", "--debug", help="Include debug output.")
def build(debug):
    """Build production assets."""
    pass

If I do from .config import Config then it will be okay. Is there any way to fix this?