pymethodbook 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,23 @@
1
+ """pymethodbook — a terminal-first reference tool for Python's built-in methods."""
2
+ from .core import get_method, load_type, search as _search
3
+ from .render import render_method, render_type_table, render_search_results
4
+
5
+ __version__ = "0.1.0"
6
+
7
+
8
+ def explain(type_name, method_name):
9
+ """Looks up one method and prints it."""
10
+ entry = get_method(type_name, method_name)
11
+ render_method(type_name, method_name, entry)
12
+
13
+
14
+ def enlist(type_name):
15
+ """Prints every curated method for a type."""
16
+ data = load_type(type_name)
17
+ render_type_table(type_name, data)
18
+
19
+
20
+ def search(keyword):
21
+ """Searches every type for a keyword and prints the results."""
22
+ results = _search(keyword)
23
+ render_search_results(keyword, results)
pymethodbook/cli.py ADDED
@@ -0,0 +1,40 @@
1
+ """Command-line interface for pymethodbook."""
2
+ import typer
3
+
4
+ from . import explain as _explain, enlist as _enlist, search as _search
5
+ from .core import TypeNotFoundError, MethodNotFoundError
6
+
7
+ app = typer.Typer(help="A terminal-first reference tool for Python's built-in methods.")
8
+
9
+
10
+ @app.command()
11
+ def explain(
12
+ type_name: str = typer.Argument(..., help="e.g. list, dict"),
13
+ method_name: str = typer.Argument(..., help="e.g. append, get"),
14
+ ):
15
+ """Show one method's description, signature, and example."""
16
+ try:
17
+ _explain(type_name, method_name)
18
+ except (TypeNotFoundError, MethodNotFoundError) as e:
19
+ typer.secho(str(e), fg=typer.colors.RED)
20
+ raise typer.Exit(code=1)
21
+
22
+
23
+ @app.command(name="list")
24
+ def list_methods(type_name: str = typer.Argument(..., help="e.g. list, dict")):
25
+ """List every curated method for a type."""
26
+ try:
27
+ _enlist(type_name)
28
+ except TypeNotFoundError as e:
29
+ typer.secho(str(e), fg=typer.colors.RED)
30
+ raise typer.Exit(code=1)
31
+
32
+
33
+ @app.command()
34
+ def search(keyword: str = typer.Argument(..., help="Keyword to search for")):
35
+ """Search every type for a keyword."""
36
+ _search(keyword)
37
+
38
+
39
+ if __name__ == "__main__":
40
+ app()
pymethodbook/core.py ADDED
@@ -0,0 +1,70 @@
1
+ """Core lookup and search logic for pymethodbook. No printing happens here —
2
+ this module only loads and finds data; pymethodbook/render.py displays it."""
3
+ import json
4
+ import difflib
5
+ from pathlib import Path
6
+
7
+ DATA_DIR = Path(__file__).parent / "data"
8
+
9
+
10
+ class TypeNotFoundError(Exception):
11
+ """Raised when a requested type has no data file."""
12
+
13
+
14
+ class MethodNotFoundError(Exception):
15
+ """Raised when a requested method isn't found for a type."""
16
+
17
+
18
+ def available_types():
19
+ """Returns a sorted list of supported type names, e.g. ['dict', 'list']."""
20
+ return sorted(p.stem for p in DATA_DIR.glob("*.json"))
21
+
22
+
23
+ def load_type(type_name):
24
+ """Loads and returns the full data dict for a type, e.g. load_type('list')."""
25
+ path = DATA_DIR / f"{type_name}.json"
26
+ if not path.exists():
27
+ types = ", ".join(available_types())
28
+ raise TypeNotFoundError(f"No data for type '{type_name}'. Available types: {types}")
29
+ with open(path, encoding="utf-8") as f:
30
+ return json.load(f)
31
+
32
+
33
+ def get_method(type_name, method_name):
34
+ """Returns the entry dict for one method. Raises MethodNotFoundError with
35
+ close-match suggestions if the name is misspelled."""
36
+ data = load_type(type_name)
37
+ if method_name in data:
38
+ return data[method_name]
39
+
40
+ close = difflib.get_close_matches(method_name, data.keys(), n=3, cutoff=0.5)
41
+ hint = f" Did you mean: {', '.join(close)}?" if close else ""
42
+ raise MethodNotFoundError(f"'{method_name}' not found on {type_name}.{hint}")
43
+
44
+
45
+ def search(keyword):
46
+ """Searches every type's methods for keyword matches in the name or
47
+ description. Returns a list of (type_name, method_name, entry) tuples —
48
+ exact name matches first, then substring matches, then fuzzy matches."""
49
+ keyword_lower = keyword.lower()
50
+ exact, substring, fuzzy = [], [], []
51
+
52
+ for type_name in available_types():
53
+ data = load_type(type_name)
54
+ already = set()
55
+
56
+ for method_name, entry in data.items():
57
+ if method_name.lower() == keyword_lower:
58
+ exact.append((type_name, method_name, entry))
59
+ already.add(method_name)
60
+ elif keyword_lower in method_name.lower() or keyword_lower in entry["description"].lower():
61
+ substring.append((type_name, method_name, entry))
62
+ already.add(method_name)
63
+
64
+ remaining = [m for m in data if m not in already]
65
+ close = difflib.get_close_matches(keyword_lower, [m.lower() for m in remaining], n=3, cutoff=0.6)
66
+ for method_name in remaining:
67
+ if method_name.lower() in close:
68
+ fuzzy.append((type_name, method_name, data[method_name]))
69
+
70
+ return exact + substring + fuzzy
@@ -0,0 +1,68 @@
1
+ {
2
+ "get": {
3
+ "signature": "dict.get(key, default=None)",
4
+ "description": "Returns the value for key if present, otherwise default, without raising an error.",
5
+ "example": "d = {\"a\": 1}\nd.get(\"b\", 0)\n# -> 0",
6
+ "category": "read-only"
7
+ },
8
+ "keys": {
9
+ "signature": "dict.keys()",
10
+ "description": "Returns a view of all keys in the dictionary.",
11
+ "example": "d = {\"a\": 1, \"b\": 2}\nlist(d.keys())\n# -> ['a', 'b']",
12
+ "category": "read-only"
13
+ },
14
+ "values": {
15
+ "signature": "dict.values()",
16
+ "description": "Returns a view of all values in the dictionary.",
17
+ "example": "d = {\"a\": 1, \"b\": 2}\nlist(d.values())\n# -> [1, 2]",
18
+ "category": "read-only"
19
+ },
20
+ "items": {
21
+ "signature": "dict.items()",
22
+ "description": "Returns a view of (key, value) pairs in the dictionary.",
23
+ "example": "d = {\"a\": 1}\nlist(d.items())\n# -> [('a', 1)]",
24
+ "category": "read-only"
25
+ },
26
+ "update": {
27
+ "signature": "dict.update(other)",
28
+ "description": "Adds key/value pairs from another dict or iterable, overwriting existing keys, in place.",
29
+ "example": "d = {\"a\": 1}\nd.update({\"b\": 2})\n# d -> {'a': 1, 'b': 2}",
30
+ "category": "mutating"
31
+ },
32
+ "pop": {
33
+ "signature": "dict.pop(key, default)",
34
+ "description": "Removes key and returns its value; returns default (or raises KeyError) if key is missing.",
35
+ "example": "d = {\"a\": 1, \"b\": 2}\nd.pop(\"a\")\n# -> 1, d -> {'b': 2}",
36
+ "category": "mutating"
37
+ },
38
+ "popitem": {
39
+ "signature": "dict.popitem()",
40
+ "description": "Removes and returns the most recently inserted (key, value) pair.",
41
+ "example": "d = {\"a\": 1, \"b\": 2}\nd.popitem()\n# -> ('b', 2)",
42
+ "category": "mutating"
43
+ },
44
+ "setdefault": {
45
+ "signature": "dict.setdefault(key, default=None)",
46
+ "description": "Returns the value for key if present; otherwise inserts key with default and returns it.",
47
+ "example": "d = {\"a\": 1}\nd.setdefault(\"b\", 2)\n# -> 2, d -> {'a': 1, 'b': 2}",
48
+ "category": "mutating"
49
+ },
50
+ "clear": {
51
+ "signature": "dict.clear()",
52
+ "description": "Removes all items from the dictionary, in place.",
53
+ "example": "d = {\"a\": 1}\nd.clear()\n# d -> {}",
54
+ "category": "mutating"
55
+ },
56
+ "copy": {
57
+ "signature": "dict.copy()",
58
+ "description": "Returns a shallow copy of the dictionary as a new, separate object.",
59
+ "example": "d = {\"a\": 1}\nd2 = d.copy()\n# d2 -> {'a': 1}",
60
+ "category": "read-only"
61
+ },
62
+ "fromkeys": {
63
+ "signature": "dict.fromkeys(iterable, value=None)",
64
+ "description": "Creates a new dict with keys from iterable, all set to the same value.",
65
+ "example": "dict.fromkeys(['a', 'b'], 0)\n# -> {'a': 0, 'b': 0}",
66
+ "category": "read-only"
67
+ }
68
+ }
@@ -0,0 +1,68 @@
1
+ {
2
+ "append": {
3
+ "signature": "list.append(item)",
4
+ "description": "Adds a single item to the end of the list, in place.",
5
+ "example": "nums = [1, 2]\nnums.append(3)\n# nums -> [1, 2, 3]",
6
+ "category": "mutating"
7
+ },
8
+ "extend": {
9
+ "signature": "list.extend(iterable)",
10
+ "description": "Adds all items from an iterable to the end of the list, in place.",
11
+ "example": "nums = [1, 2]\nnums.extend([3, 4])\n# nums -> [1, 2, 3, 4]",
12
+ "category": "mutating"
13
+ },
14
+ "insert": {
15
+ "signature": "list.insert(index, item)",
16
+ "description": "Inserts an item at the given index, shifting later items to the right.",
17
+ "example": "nums = [1, 3]\nnums.insert(1, 2)\n# nums -> [1, 2, 3]",
18
+ "category": "mutating"
19
+ },
20
+ "remove": {
21
+ "signature": "list.remove(value)",
22
+ "description": "Removes the first item equal to value; raises ValueError if it isn't found.",
23
+ "example": "nums = [1, 2, 3, 2]\nnums.remove(2)\n# nums -> [1, 3, 2]",
24
+ "category": "mutating"
25
+ },
26
+ "pop": {
27
+ "signature": "list.pop(index=-1)",
28
+ "description": "Removes and returns the item at index (the last item by default).",
29
+ "example": "nums = [1, 2, 3]\nlast = nums.pop()\n# last -> 3, nums -> [1, 2]",
30
+ "category": "mutating"
31
+ },
32
+ "clear": {
33
+ "signature": "list.clear()",
34
+ "description": "Removes all items from the list, in place.",
35
+ "example": "nums = [1, 2, 3]\nnums.clear()\n# nums -> []",
36
+ "category": "mutating"
37
+ },
38
+ "index": {
39
+ "signature": "list.index(value, start=0, end=len(list))",
40
+ "description": "Returns the index of the first item equal to value; raises ValueError if not found.",
41
+ "example": "nums = [10, 20, 30]\nnums.index(20)\n# -> 1",
42
+ "category": "read-only"
43
+ },
44
+ "count": {
45
+ "signature": "list.count(value)",
46
+ "description": "Returns how many times value appears in the list.",
47
+ "example": "nums = [1, 2, 2, 3]\nnums.count(2)\n# -> 2",
48
+ "category": "read-only"
49
+ },
50
+ "sort": {
51
+ "signature": "list.sort(key=None, reverse=False)",
52
+ "description": "Sorts the list in place.",
53
+ "example": "nums = [3, 1, 2]\nnums.sort()\n# nums -> [1, 2, 3]",
54
+ "category": "mutating"
55
+ },
56
+ "reverse": {
57
+ "signature": "list.reverse()",
58
+ "description": "Reverses the order of the list, in place.",
59
+ "example": "nums = [1, 2, 3]\nnums.reverse()\n# nums -> [3, 2, 1]",
60
+ "category": "mutating"
61
+ },
62
+ "copy": {
63
+ "signature": "list.copy()",
64
+ "description": "Returns a shallow copy of the list as a new, separate object.",
65
+ "example": "nums = [1, 2, 3]\nnums2 = nums.copy()\n# nums2 -> [1, 2, 3]",
66
+ "category": "read-only"
67
+ }
68
+ }
pymethodbook/render.py ADDED
@@ -0,0 +1,50 @@
1
+ """Terminal presentation layer for pymethodbook, built on `rich`.
2
+ A single shared Console auto-detects non-terminal output (e.g. piped to a
3
+ file or `| cat`) and drops ANSI color codes automatically — no extra
4
+ handling needed."""
5
+ from rich.console import Console
6
+ from rich.panel import Panel
7
+ from rich.table import Table
8
+ from rich.syntax import Syntax
9
+
10
+ console = Console()
11
+
12
+
13
+ def render_method(type_name, method_name, entry):
14
+ """Prints one method's description, signature, and example."""
15
+ code = Syntax(entry["example"], "python", theme="ansi_dark", line_numbers=False)
16
+ panel = Panel(code, title=f"{type_name}.{method_name}", subtitle=entry["signature"])
17
+ console.print(f"[bold]{entry['description']}[/bold]")
18
+ console.print(panel)
19
+ console.print(f"[dim]category: {entry['category']}[/dim]")
20
+
21
+
22
+ def render_type_table(type_name, data):
23
+ """Prints every method for a type as a table, sorted by category then name."""
24
+ table = Table(title=f"{type_name} methods")
25
+ table.add_column("Method", style="bold")
26
+ table.add_column("Signature")
27
+ table.add_column("Category")
28
+ table.add_column("Description")
29
+
30
+ for method_name, entry in sorted(data.items(), key=lambda kv: (kv[1]["category"], kv[0])):
31
+ table.add_row(method_name, entry["signature"], entry["category"], entry["description"])
32
+
33
+ console.print(table)
34
+
35
+
36
+ def render_search_results(keyword, results):
37
+ """Prints search results across types as a table."""
38
+ if not results:
39
+ console.print(f"[yellow]No matches for '{keyword}'.[/yellow]")
40
+ return
41
+
42
+ table = Table(title=f"Search results for '{keyword}'")
43
+ table.add_column("Type", style="bold")
44
+ table.add_column("Method")
45
+ table.add_column("Description")
46
+
47
+ for type_name, method_name, entry in results:
48
+ table.add_row(type_name, method_name, entry["description"])
49
+
50
+ console.print(table)
@@ -0,0 +1,76 @@
1
+ Metadata-Version: 2.5
2
+ Name: pymethodbook
3
+ Version: 0.1.0
4
+ Summary: A terminal-first reference tool for Python's built-in methods — curated descriptions, runnable examples, and a searchable CLI.
5
+ Project-URL: Homepage, https://github.com/logeshchandrasekar/pymethodbook
6
+ Project-URL: Repository, https://github.com/logeshchandrasekar/pymethodbook
7
+ Project-URL: Issues, https://github.com/logeshchandrasekar/pymethodbook/issues
8
+ Author-email: Logesh Chandrasekar <logeshwarchandrasekar@gmail.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: builtins,cheatsheet,cli,documentation,python,reference
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.8
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Topic :: Software Development :: Documentation
24
+ Requires-Python: >=3.8
25
+ Requires-Dist: rich>=13.0
26
+ Requires-Dist: typer>=0.9
27
+ Description-Content-Type: text/markdown
28
+
29
+ # pymethodbook
30
+
31
+ A terminal-first reference tool for Python's built-in methods — curated descriptions, runnable examples, and a searchable CLI. No more digging through docs for what `list.sort()` actually does.
32
+
33
+ ## Why
34
+
35
+ `dir()` and `help()` give you names and terse docstrings. pymethodbook gives you a one-line description, a runnable example, and a category tag for every method — right in your terminal, in code or from the command line.
36
+
37
+ ## Install
38
+
39
+ ```bash
40
+ pip install pymethodbook
41
+ ```
42
+
43
+ ## Usage
44
+
45
+ ### As a CLI
46
+
47
+ ```bash
48
+ pymethodbook explain list append
49
+ pymethodbook list dict
50
+ pymethodbook search copy
51
+ ```
52
+
53
+ ### As a library
54
+
55
+ ```python
56
+ from pymethodbook import explain, enlist, search
57
+
58
+ explain("list", "append")
59
+ enlist("dict")
60
+ search("copy")
61
+ ```
62
+
63
+ ## Currently covers
64
+
65
+ - `list` — 11 methods
66
+ - `dict` — 11 methods
67
+
68
+ More types (`str`, `set`, `tuple`) are on the roadmap — see [ROADMAP.md](ROADMAP.md).
69
+
70
+ ## Contributing
71
+
72
+ Adding a method doesn't require touching any Python code — see [CONTRIBUTING.md](CONTRIBUTING.md).
73
+
74
+ ## License
75
+
76
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,11 @@
1
+ pymethodbook/__init__.py,sha256=MKkwGDqZgbpTTp4et_a24mncHFuy7R-ZIqks5lpAwNk,757
2
+ pymethodbook/cli.py,sha256=6DQ7pnA4TZYtnxUAYDPOZuK9E_A4kt0dwdU8epfU8RY,1248
3
+ pymethodbook/core.py,sha256=mRriqTvbEYAy6STYqIqf6BMQc4K9YgiEUPzwPE1HfAM,2778
4
+ pymethodbook/render.py,sha256=vpAa4mweD2TZPGoeDctSPEHnOvgKhO_2D1hW1qpjrAs,1925
5
+ pymethodbook/data/dict.json,sha256=FlNJ--p04yE_mvh7P0mVmNjh6dy6pUGbKyfCBlNN66Q,2799
6
+ pymethodbook/data/list.json,sha256=s18FcDhDnEwFoflCMBc4aPP_3izKKn5AX8r6qnz5Q6E,2688
7
+ pymethodbook-0.1.0.dist-info/METADATA,sha256=hVWEf6xYZCPre7hGYGcZGqR5HxRRdo3zE0xXCiQPpU0,2372
8
+ pymethodbook-0.1.0.dist-info/WHEEL,sha256=THafob7ofN-NsuMN7Mg4qZyHaQI7KkD-QlcQatYhXPo,87
9
+ pymethodbook-0.1.0.dist-info/entry_points.txt,sha256=yP7jg69aoOsjC8UayW5Z-HzRs_aa2eV9qXe3BTsA_jU,54
10
+ pymethodbook-0.1.0.dist-info/licenses/LICENSE,sha256=Kzkh8ShsbFuYXeEIHQNink4JQTWC8FDIGo3hGaZIN1U,1100
11
+ pymethodbook-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.3
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ pymethodbook = pymethodbook.cli:app
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Logeshwar Chandrasekar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.