Skip to content

Commit

Permalink
Merge pull request #196 from adamtheturtle/language-protocol
Browse files Browse the repository at this point in the history
Resolve coverage ignore
  • Loading branch information
adamtheturtle authored Nov 5, 2024
2 parents cfffa3c + cd33872 commit df20451
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 75 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ Changelog
Next
----

* Error if files do not have a ``rst`` or ``md`` extension.

2024.11.04
----------

* Add options to control whether a pseudo-terminal is used for running commands in.
* Rename some options to make it clear that they apply to the temporary files created.

2024.10.14
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ omit = [
[tool.coverage.report]
exclude_also = [
"if TYPE_CHECKING:",
"class .*\\bProtocol\\):",
]

[tool.mypy]
Expand Down
80 changes: 5 additions & 75 deletions src/doccmd/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,9 @@
from pygments.lexers import get_all_lexers
from sybil import Sybil
from sybil.evaluators.skip import Skipper
from sybil.parsers.myst import CodeBlockParser as MystCodeBlockParser
from sybil.parsers.rest import CodeBlockParser as RestCodeBlockParser
from sybil_extras.evaluators.shell_evaluator import ShellCommandEvaluator
from sybil_extras.parsers.myst.custom_directive_skip import (
CustomDirectiveSkipParser as MystCustomDirectiveSkipParser,
)
from sybil_extras.parsers.rest.custom_directive_skip import (
CustomDirectiveSkipParser as RestCustomDirectiveSkipParser,
)

from ._languages import UnknownMarkupLanguageError, get_markup_language

if TYPE_CHECKING:
from sybil.typing import Parser
Expand Down Expand Up @@ -131,70 +125,6 @@ def _get_skip_directives(skip_markers: Iterable[str]) -> Sequence[str]:
return skip_directives


@beartype
class _UnknownMarkupLanguageError(Exception):
"""
Raised when the markup language is not recognized.
"""

def __init__(self, file_path: Path) -> None:
"""
Args:
file_path: The file path for which the markup language is unknown.
"""
super().__init__(f"Markup language not known for {file_path}.")


@beartype
@unique
class _MarkupLanguage(Enum):
"""
Supported markup languages.
"""

MYST = auto()
RESTRUCTURED_TEXT = auto()

@classmethod
def from_file_path(cls, file_path: Path) -> "_MarkupLanguage":
"""
Determine the markup language from the file path.
"""
if file_path.suffix == ".md":
return cls.MYST
if file_path.suffix == ".rst":
return cls.RESTRUCTURED_TEXT
raise _UnknownMarkupLanguageError(file_path=file_path)

@property
def skip_parser_cls(
self,
) -> type[MystCustomDirectiveSkipParser | RestCustomDirectiveSkipParser]:
"""
Skip parser class.
"""
match self:
case _MarkupLanguage.MYST:
return MystCustomDirectiveSkipParser
# Ignore coverage because this never not reached.
case _MarkupLanguage.RESTRUCTURED_TEXT: # pragma: no cover
return RestCustomDirectiveSkipParser

@property
def code_block_parser_cls(
self,
) -> type[MystCodeBlockParser | RestCodeBlockParser]:
"""
Skip parser class.
"""
match self:
case _MarkupLanguage.MYST:
return MystCodeBlockParser
# Ignore coverage because this never not reached.
case _MarkupLanguage.RESTRUCTURED_TEXT: # pragma: no cover
return RestCodeBlockParser


@beartype
def _get_temporary_file_extension(
language: str,
Expand Down Expand Up @@ -229,7 +159,7 @@ def _run_args_against_docs(
"""
Run commands on the given file.
"""
markup_language = _MarkupLanguage.from_file_path(file_path=document_path)
markup_language = get_markup_language(file_path=document_path)
temporary_file_extension = _get_temporary_file_extension(
language=code_block_language,
given_file_extension=temporary_file_extension,
Expand Down Expand Up @@ -461,8 +391,8 @@ def main(

try:
for document_path in document_paths:
_MarkupLanguage.from_file_path(file_path=document_path)
except _UnknownMarkupLanguageError as exc:
get_markup_language(file_path=document_path)
except UnknownMarkupLanguageError as exc:
raise click.UsageError(message=str(exc)) from exc

for document_path in document_paths:
Expand Down
102 changes: 102 additions & 0 deletions src/doccmd/_languages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
"""
Tools for managing markup languages.
"""

from dataclasses import dataclass
from pathlib import Path
from typing import ClassVar, Protocol, runtime_checkable

from beartype import beartype
from sybil.parsers.myst import CodeBlockParser as MystCodeBlockParser
from sybil.parsers.rest import CodeBlockParser as RestCodeBlockParser
from sybil_extras.parsers.myst.custom_directive_skip import (
CustomDirectiveSkipParser as MystCustomDirectiveSkipParser,
)
from sybil_extras.parsers.rest.custom_directive_skip import (
CustomDirectiveSkipParser as RestCustomDirectiveSkipParser,
)


@beartype
class UnknownMarkupLanguageError(Exception):
"""
Raised when the markup language is not recognized.
"""

def __init__(self, file_path: Path) -> None:
"""
Args:
file_path: The file path for which the markup language is unknown.
"""
super().__init__(f"Markup language not known for {file_path}.")


@runtime_checkable
class _MarkupLanguage(Protocol):
"""
A protocol for markup languages.
"""

@property
def skip_parser_cls(
self,
) -> type[MystCustomDirectiveSkipParser | RestCustomDirectiveSkipParser]:
"""
Skip parser class.
"""
# We disable a pylint warning here because the ellipsis is required
# for pyright to recognize this as a protocol.
... # pylint: disable=unnecessary-ellipsis

@property
def code_block_parser_cls(
self,
) -> type[MystCodeBlockParser | RestCodeBlockParser]:
"""
Skip parser class.
"""
# We disable a pylint warning here because the ellipsis is required
# for pyright to recognize this as a protocol.
... # pylint: disable=unnecessary-ellipsis


@beartype
@dataclass(frozen=True)
class _MyST:
"""
The MyST markup language.
"""

skip_parser_cls: ClassVar[type[MystCustomDirectiveSkipParser]] = (
MystCustomDirectiveSkipParser
)
code_block_parser_cls: ClassVar[type[MystCodeBlockParser]] = (
MystCodeBlockParser
)


@beartype
@dataclass(frozen=True)
class _ReStructuredText:
"""
The reStructuredText markup language.
"""

skip_parser_cls: ClassVar[type[RestCustomDirectiveSkipParser]] = (
RestCustomDirectiveSkipParser
)
code_block_parser_cls: ClassVar[type[RestCodeBlockParser]] = (
RestCodeBlockParser
)


@beartype
def get_markup_language(file_path: Path) -> _MarkupLanguage:
"""
Determine the markup language from the file path.
"""
if file_path.suffix == ".md":
return _MyST
if file_path.suffix == ".rst":
return _ReStructuredText
raise UnknownMarkupLanguageError(file_path=file_path)

0 comments on commit df20451

Please sign in to comment.