-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
35f8c7a
commit 3119cd6
Showing
9 changed files
with
371 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
repos: | ||
- repo: https://github.com/pre-commit/pre-commit-hooks | ||
rev: v4.5.0 | ||
hooks: | ||
- id: end-of-file-fixer | ||
- id: check-case-conflict | ||
- id: check-executables-have-shebangs | ||
- id: check-added-large-files | ||
- id: check-case-conflict | ||
- id: check-toml | ||
- id: check-yaml | ||
- id: debug-statements | ||
- id: check-builtin-literals | ||
- id: trailing-whitespace | ||
- id: check-merge-conflict | ||
- repo: https://github.com/astral-sh/ruff-pre-commit | ||
rev: v0.2.0 | ||
hooks: | ||
- id: ruff | ||
args: ["--fix"] | ||
- id: ruff-format |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,24 @@ | ||
# ipython-markdown-inspector | ||
# ipython-markdown-inspector | ||
|
||
|
||
## Installation | ||
|
||
``` | ||
pip install ipython-markdown-inspector | ||
``` | ||
|
||
## Usage | ||
|
||
To load an extension while IPython is running, use the `%load_ext` magic: | ||
|
||
``` | ||
%load_ext ipython_markdown_inspector | ||
``` | ||
|
||
To load it each time IPython starts, list it in your [configuration file](https://ipython.readthedocs.io/en/stable/config/intro.html): | ||
|
||
```python | ||
c.InteractiveShellApp.extensions = [ | ||
'ipython_markdown_inspector' | ||
] | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
from functools import partial | ||
from typing import Any, Optional, Union | ||
|
||
from IPython.core.interactiveshell import InteractiveShell | ||
from IPython.core.oinspect import OInfo | ||
|
||
from ._hook_data import InspectorHookData | ||
from .formatter import as_markdown | ||
|
||
|
||
def hook( | ||
obj_or_data: Union[InspectorHookData, Any], | ||
info: Optional[OInfo] = None, | ||
*_, | ||
ipython: InteractiveShell, | ||
) -> str: | ||
if isinstance(obj_or_data, InspectorHookData): | ||
data = obj_or_data | ||
else: | ||
# fallback for IPython 8.21 | ||
obj = obj_or_data | ||
detail_level = 0 | ||
omit_sections = [] | ||
info_dict = ipython.inspector.info( | ||
obj, "", info=info, detail_level=detail_level | ||
) | ||
data = InspectorHookData( | ||
obj=obj, | ||
info=info, | ||
info_dict=info_dict, | ||
detail_level=detail_level, | ||
omit_sections=omit_sections, | ||
) | ||
return as_markdown(data) | ||
|
||
|
||
ORIGINAL_HOOK = None | ||
|
||
|
||
def load_ipython_extension(ipython: InteractiveShell): | ||
global ORIGINAL_HOOK | ||
ORIGINAL_HOOK = ipython.inspector.mime_hooks.get("text/markdown", None) | ||
ipython.inspector.mime_hooks["text/markdown"] = partial(hook, ipython=ipython) | ||
|
||
|
||
def unload_ipython_extension(ipython: InteractiveShell): | ||
if ORIGINAL_HOOK is None: | ||
del ipython.inspector.mime_hooks["text/markdown"] | ||
else: | ||
ipython.inspector.mime_hooks["text/markdown"] = ORIGINAL_HOOK | ||
|
||
|
||
__all__ = [] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
try: | ||
from IPython.core.oinspect import InspectorHookData | ||
except ImportError: | ||
# TODO: remove once we require a version which includes | ||
# https://github.com/ipython/ipython/pull/14342 | ||
from ._ipython_patch import InspectorHookData | ||
|
||
|
||
__all__ = ["InspectorHookData"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
from typing import Any, TypedDict, Optional | ||
from dataclasses import dataclass | ||
|
||
from IPython.core.oinspect import OInfo | ||
|
||
|
||
class InfoDict(TypedDict): | ||
type_name: Optional[str] | ||
base_class: Optional[str] | ||
string_form: Optional[str] | ||
namespace: Optional[str] | ||
length: Optional[str] | ||
file: Optional[str] | ||
definition: Optional[str] | ||
docstring: Optional[str] | ||
source: Optional[str] | ||
init_definition: Optional[str] | ||
class_docstring: Optional[str] | ||
init_docstring: Optional[str] | ||
call_def: Optional[str] | ||
call_docstring: Optional[str] | ||
subclasses: Optional[str] | ||
# These won't be printed but will be used to determine how to | ||
# format the object | ||
ismagic: bool | ||
isalias: bool | ||
isclass: bool | ||
found: bool | ||
name: str | ||
|
||
|
||
@dataclass | ||
class InspectorHookData: | ||
"""Data passed to the mime hook""" | ||
|
||
obj: Any | ||
info: Optional[OInfo] | ||
info_dict: InfoDict | ||
detail_level: int | ||
omit_sections: list[str] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
from typing import Dict, List | ||
|
||
import docstring_to_markdown | ||
from IPython.core.oinspect import is_simple_callable | ||
|
||
from ._hook_data import InspectorHookData | ||
from .models import Field, CodeField, DocField, RowField | ||
|
||
|
||
FIELDS: Dict[str, List[Field]] = { | ||
"alias": [ | ||
CodeField(label="Repr", key="string_form"), | ||
], | ||
"magic": [ | ||
DocField(label="Docstring", key="docstring"), | ||
CodeField(label="Source", key="source", min_level=1), | ||
RowField(label="File", key="file"), | ||
], | ||
"class_or_callable": [ | ||
# Functions, methods, classes | ||
CodeField(label="Signature", key="definition"), | ||
CodeField(label="Init signature", key="init_definition"), | ||
DocField(label="Docstring", key="docstring"), | ||
CodeField(label="Source", key="source", min_level=1), | ||
DocField(label="Init docstring", key="init_docstring"), | ||
RowField(label="File", key="file"), | ||
RowField(label="Type", key="type_name"), | ||
RowField(label="Subclasses", key="subclasses"), | ||
], | ||
"default": [ | ||
# General Python objects | ||
CodeField(label="Signature", key="definition"), | ||
CodeField(label="Call signature", key="call_def"), | ||
RowField(label="Type", key="type_name"), | ||
RowField(label="String form", key="string_form"), | ||
RowField(label="Namespace", key="namespace"), | ||
RowField(label="Length", key="length"), | ||
RowField(label="File", key="file"), | ||
DocField(label="Docstring", key="docstring"), | ||
CodeField(label="Source", key="source", min_level=1), | ||
DocField(label="Class docstring", key="class_docstring"), | ||
DocField(label="Init docstring", key="init_docstring"), | ||
DocField(label="Call docstring", key="call_docstring"), | ||
], | ||
} | ||
|
||
|
||
TABLE_STARTER = """\ | ||
| <!-- --> | <!-- --> | | ||
|----------|----------|\ | ||
""" | ||
|
||
|
||
def markdown_formatter(text: str): | ||
try: | ||
converted = docstring_to_markdown.convert(text) | ||
return converted | ||
except docstring_to_markdown.UnknownFormatError: | ||
return f"<pre>{text}</pre>" | ||
|
||
|
||
def code_formatter(code, language="python"): | ||
return f"```{language}\n{code}\n```" | ||
|
||
|
||
def as_markdown(data: InspectorHookData) -> str: | ||
if data.info and not data.info.found: | ||
return str(data.info) | ||
|
||
info_dict = data.info_dict | ||
|
||
if info_dict["namespace"] == "Interactive": | ||
info_dict["namespace"] = None | ||
|
||
# TODO: maybe remove docstring from source? | ||
# info_dict["source"] = remove_docstring(source) | ||
|
||
if info_dict["isalias"]: | ||
fields = FIELDS["alias"] | ||
elif info_dict["ismagic"]: | ||
fields = FIELDS["magic"] | ||
if info_dict["isclass"] or is_simple_callable(data.obj): | ||
fields = FIELDS["class_or_callable"] | ||
else: | ||
fields = FIELDS["default"] | ||
|
||
chunks = [] | ||
|
||
in_table = False | ||
for field in fields: | ||
value = info_dict.get(field.key) | ||
if value is None: | ||
continue | ||
if field.kind == "row": | ||
if not in_table: | ||
in_table = True | ||
chunks.append(TABLE_STARTER) | ||
chunks[-1] += f"\n| {field.label} | `{value}` |" | ||
if field.kind == "code": | ||
chunks.append(f"#### {field.label}\n\n" + code_formatter(value)) | ||
if field.kind == "doc": | ||
chunks.append(f"#### {field.label}\n\n" + markdown_formatter(value)) | ||
|
||
return "\n\n".join(chunks) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
from dataclasses import dataclass | ||
from typing import Literal | ||
|
||
|
||
@dataclass | ||
class Field: | ||
key: str | ||
label: str | ||
kind: str | ||
min_level: int = 0 | ||
|
||
|
||
@dataclass | ||
class RowField(Field): | ||
kind: Literal["row"] = "row" | ||
|
||
|
||
@dataclass | ||
class CodeField(Field): | ||
kind: Literal["code"] = "code" | ||
|
||
|
||
@dataclass | ||
class DocField(Field): | ||
kind: Literal["doc"] = "doc" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
from IPython.core.interactiveshell import InteractiveShell | ||
from IPython import get_ipython | ||
import pytest | ||
|
||
from ipython_markdown_inspector.formatter import as_markdown | ||
from ipython_markdown_inspector._hook_data import InspectorHookData | ||
|
||
|
||
def simple_func(arg): | ||
"""Calls :py:func:`bool` on ``arg``""" | ||
return bool(arg) | ||
|
||
|
||
class SimpleClass: | ||
pass | ||
|
||
|
||
@pytest.mark.parametrize( | ||
"object_name, part", | ||
[ | ||
["%%python", "Run cells with python"], | ||
["simple_func", "Calls `bool` on ``arg``"], | ||
["simple_func", "| Type | `function` |"], | ||
["test_int", "| Type | `int` |"], | ||
["test_int", "| String form | `1` |"], | ||
["test_str", "| Type | `str` |"], | ||
["test_str", "| String form | `a` |"], | ||
["test_str", "| Length | `1` |"], | ||
["simple_cls", "| Type | `type` |"], | ||
["simple_instance", "| Type | `SimpleClass` |"], | ||
], | ||
) | ||
def test_result_contains(object_name, part): | ||
ip: InteractiveShell = get_ipython() # type: ignore | ||
ip.user_ns["test_str"] = "a" | ||
ip.user_ns["test_int"] = 1 | ||
ip.user_ns["simple_func"] = simple_func | ||
ip.user_ns["simple_cls"] = SimpleClass | ||
ip.user_ns["simple_instance"] = SimpleClass() | ||
oinfo = ip._object_find(object_name) | ||
detail_level = 0 | ||
info_dict = ip.inspector.info(oinfo.obj, object_name) | ||
data = InspectorHookData( | ||
obj=oinfo.obj, | ||
info=oinfo, | ||
info_dict=info_dict, | ||
detail_level=detail_level, | ||
omit_sections=[], | ||
) | ||
assert part in as_markdown(data) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
[build-system] | ||
requires = ["hatchling"] | ||
build-backend = "hatchling.build" | ||
|
||
[project] | ||
name = "ipython-markdown-inspector" | ||
version = "0.0.0" | ||
dependencies = [ | ||
"ipython>=8.21.0", | ||
"docstring-to-markdown>=0.13,<1.0" | ||
] | ||
requires-python = ">=3.8" | ||
authors = [ | ||
{name = "Michał Krassowski"} | ||
] | ||
description = "" | ||
readme = "README.md" | ||
license = {file = "LICENSE"} | ||
keywords = ["IPython", "markdown", "inpsector", "docstring"] | ||
classifiers = [ | ||
"Framework :: IPython", | ||
"Framework :: Jupyter", | ||
"Development Status :: 4 - Beta", | ||
"Intended Audience :: Developers", | ||
"Intended Audience :: Science/Research", | ||
"License :: OSI Approved :: BSD License", | ||
"Programming Language :: Python", | ||
"Programming Language :: Python :: 3", | ||
"Programming Language :: Python :: 3 :: Only", | ||
] | ||
|
||
[project.urls] | ||
Repository = "https://github.com/krassowski/ipython-markdown-inspector.git" | ||
"Bug Tracker" = "https://github.com/krassowski/ipython-markdown-inspector/issues" | ||
Changelog = "https://github.com/krassowski/ipython-markdown-inspector/blob/main/CHANGELOG.md" | ||
|
||
[project.optional-dependencies] | ||
test = [ | ||
"pytest" | ||
] | ||
dev = [ | ||
"build", | ||
"pre-commit", | ||
"ruff==0.2.0" | ||
] |