Beispiel #1
0
def py2md(module: str, out: Optional[str] = None) -> bool:
    """
    Returns `True` if module successfully processed, otherwise `False`.
    """
    logger.debug("Processing %s", module)
    pydocmd = PydocMarkdown(
        loaders=[PythonLoader(modules=[module])],
        processors=[AllenNlpFilterProcessor(),
                    AllenNlpDocstringProcessor()],
        renderer=AllenNlpRenderer(
            filename=out,
            add_method_class_prefix=False,
            add_member_class_prefix=False,
            data_code_block=True,
            signature_with_def=True,
            use_fixed_header_levels=False,
            render_module_header=False,
            descriptive_class_title=False,
        ),
    )
    if out:
        out_path = Path(out)
        os.makedirs(out_path.parent, exist_ok=True)

    pydocmd.load_modules()
    try:
        pydocmd.process()
    except DocstringError as err:
        logger.exception("Failed to process %s.\n%s", module, err)
        return False
    pydocmd.render()
    return True
def test_full_processing_custom_top_level_names():
    docs_path = Path(__file__).parent / "test_package" / "docs"
    config = PydocMarkdown(
        loaders=[
            PythonLoader(search_path=[str(Path(__file__).parent.resolve())],
                         packages=['test_package'])
        ],
        processors=[
            FilterProcessor(skip_empty_modules=True),
            CrossrefProcessor(),
            SmartProcessor()
        ],
        renderer=DocusaurusRenderer(
            docs_base_path=str(docs_path.resolve()),
            sidebar_top_level_label=None,
            sidebar_top_level_module_label="My test package"))

    modules = config.load_modules()
    config.process(modules)
    config.render(modules)

    sidebar = docs_path / "reference" / "sidebar.json"
    assert sidebar.exists()

    with sidebar.open("r") as handle:
        sidebar = json.load(handle)

    assert sidebar == {
        "items": [{
            "items": [
                "reference/test_package/module/__init__",
                "reference/test_package/module/stuff"
            ],
            "label":
            "test_package.module",
            "type":
            "category"
        }],
        "label":
        "My test package",
        "type":
        "category"
    }
Beispiel #3
0
def py2md(module: str, out: Optional[str] = None) -> None:
    pydocmd = PydocMarkdown(
        loaders=[PythonLoader(modules=[module])],
        processors=[AllenNlpFilterProcessor(), AllenNlpDocstringProcessor()],
        renderer=AllenNlpRenderer(
            filename=out,
            add_method_class_prefix=False,
            add_member_class_prefix=False,
            data_code_block=True,
            signature_with_def=True,
            use_fixed_header_levels=False,
            render_module_header=False,
        ),
    )
    if out:
        out_path = Path(out)
        os.makedirs(out_path.parent, exist_ok=True)

    pydocmd.load_modules()
    pydocmd.process()
    pydocmd.render()
    logging.info("Processed %s", module)
Beispiel #4
0
class PydocMarkdown(Struct):
  """
  This object represents the main configuration for Pydoc-Markdown.
  """

  #: A list of loader implementations that load #docspec.Module#s.
  #: Defaults to #PythonLoader.
  loaders = Field([Loader], default=lambda: [PythonLoader()])

  #: A list of processor implementations that modify #docspec.Module#s. Defaults
  #: to #FilterProcessor, #SmartProcessor and #CrossrefProcessor.
  processors = Field([Processor], default=lambda: [
    FilterProcessor(), SmartProcessor(), CrossrefProcessor()])

  #: A renderer for #docspec.Module#s. Defaults to #MarkdownRenderer.
  renderer = Field(Renderer, default=MarkdownRenderer)

  #: Hooks that can be executed at certain points in the pipeline. The commands
  #: are executed with the current `SHELL`.
  hooks = Field({
    'pre_render': Field([str], FieldName('pre-render'), default=list),
    'post_render': Field([str], FieldName('post-render'), default=list),
  }, default=Field.DEFAULT_CONSTRUCT)

  # Hidden fields are filled at a later point in time and are not (de-) serialized.
  unknown_fields = Field([str], default=list, hidden=True)

  def __init__(self, *args, **kwargs) -> None:
    super(PydocMarkdown, self).__init__(*args, **kwargs)
    self.resolver: Optional[Resolver] = None
    self._context: Optional[Context] = None

  def load_config(self, data: Union[str, dict]) -> None:
    """
    Loads a YAML configuration from *data*.

    :param data: Nested structurre or the path to a YAML configuration file.
    """

    filename = None
    if isinstance(data, str):
      filename = data
      logger.info('Loading configuration file "%s".', filename)
      data = ytemplate.load(filename, {'env': ytemplate.Attributor(os.environ)})

    collector = Collect()
    result = mapper.deserialize(data, type(self), filename=filename, decorations=[collector])
    vars(self).update(vars(result))

    self.unknown_fields = list(concat((str(n.locator.append(u)) for u in n.unknowns)
      for n in collector.nodes))

  def init(self, context: Context) -> None:
    """
    Initialize all plugins with the specified *context*. Cannot be called multiple times.
    If omitted, the plugins will be initialized with a default context before the load,
    process or render phase.
    """

    if self._context:
      raise RuntimeError('already initialized')
    self._context = context
    logger.debug('Initializing plugins with context %r', context)
    for loader in self.loaders:
      loader.init(context)
    for processor in self.processors:
      processor.init(context)
    self.renderer.init(context)

  def ensure_initialized(self) -> None:
    if not self._context:
      self.init(Context(directory='.'))

  def load_modules(self) -> List[docspec.Module]:
    """
    Loads modules via the #loaders.
    """

    logger.info('Loading modules.')
    self.ensure_initialized()
    modules = []
    for loader in self.loaders:
      modules.extend(loader.load())
    return modules

  def process(self, modules: List[docspec.Module]) -> None:
    """
    Process modules via the #processors.
    """

    self.ensure_initialized()
    if self.resolver is None:
      self.resolver = self.renderer.get_resolver(modules)
    for processor in self.processors:
      processor.process(modules, self.resolver)

  def render(self, modules: List[docspec.Module], run_hooks: bool = True) -> None:
    """
    Render modules via the #renderer.
    """

    self.ensure_initialized()
    if run_hooks:
      self.run_hooks('pre-render')
    if self.resolver is None:
      self.resolver = self.renderer.get_resolver(modules)
    self.renderer.process(modules, self.resolver)
    self.renderer.render(modules)
    if run_hooks:
      self.run_hooks('post-render')

  def build(self, site_dir: str) -> None:
    if not Builder.provided_by(self.renderer):
      name = type(self.renderer).__name__
      raise NotImplementedError('Renderer "{}" does not support building'.format(name))
    self.ensure_initialized()
    self.renderer.build(site_dir)

  def run_hooks(self, hook_name: str) -> None:
    assert self._context is not None
    for command in getattr(self.hooks, hook_name.replace('-', '_')):
      subprocess.check_call(command, shell=True, cwd=self._context.directory)
def test_full_processing():
    docs_path = Path(__file__).parent / "test_package" / "docs"
    config = PydocMarkdown(loaders=[
        PythonLoader(search_path=[str(Path(__file__).parent.resolve())],
                     packages=['test_package'])
    ],
                           processors=[
                               FilterProcessor(skip_empty_modules=True),
                               CrossrefProcessor(),
                               SmartProcessor()
                           ],
                           renderer=DocusaurusRenderer(
                               docs_base_path=str(docs_path.resolve()),
                               sidebar_top_level_label="Code reference"))

    modules = config.load_modules()
    config.process(modules)
    config.render(modules)

    sidebar = docs_path / "reference" / "sidebar.json"
    init_md = docs_path / "reference" / "test_package" / "module" / "__init__.md"
    wrong_module_init_md = docs_path / "reference" / "test_package" / "module.md"
    suff_md = docs_path / "reference" / "test_package" / "module" / "stuff.md"
    assert (docs_path / "reference").is_dir()
    assert sidebar.exists()
    assert suff_md.exists()
    assert init_md.exists()
    assert not wrong_module_init_md.exists()
    assert not (docs_path / "reference" / "test_package" /
                "no_docstrings.md").exists()

    with sidebar.open("r") as handle:
        sidebar = json.load(handle)

    assert sidebar == {
        "items": [{
            "items": [{
                "items": [
                    "reference/test_package/module/__init__",
                    "reference/test_package/module/stuff"
                ],
                "label":
                "test_package.module",
                "type":
                "category"
            }],
            "label":
            "test_package",
            "type":
            "category"
        }],
        "label":
        "Code reference",
        "type":
        "category"
    }

    with suff_md.open("r") as handle:
        stuff_doc = handle.read()

    assert_text_equals(
        stuff_doc, r"""---
sidebar_label: stuff
title: test_package.module.stuff
---

This is a module about stuff.

#### CONSTANT

this is a constant about stuff

#### my\_funct

```python
my_funct()
```

Do something, or nothing.

## CoolStuff Objects

```python
class CoolStuff()
```

Super cool stuff.

#### cool\_attr

This is a cool attribute.

#### run\_cool\_stuff

```python
 | run_cool_stuff()
```

Run cool stuff
""")

    with init_md.open("r") as handle:
        init_doc = handle.read()

    assert_text_equals(
        init_doc, r"""---
sidebar_label: module
title: test_package.module
---

This is module __init__.py

#### CONSTANT

This constant is rad

""")