Skip to content

Commit 0d04fb2

Browse files
committed
✨ Support ptpython/ipython/ptipython
1 parent 53d9066 commit 0d04fb2

File tree

12 files changed

+324
-76
lines changed

12 files changed

+324
-76
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ repos:
5757
args:
5858
- --number
5959
additional_dependencies:
60+
- mdformat-gfm
6061
- mdformat-myst
6162
- mdformat-toc
6263
- mdformat-deflist

CITATION.cff

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ authors:
66
- family-names: Wu
77
given-names: Zhenyu
88
orcid: https://orcid.org/0000-0001-6478-9993
9-
title: "python-wakatime: wakatime plugin for python"
9+
title: "python-wakatime: wakatime plugin for python REPLs"
1010
date-released: 2023-01-12
1111
url: https://github.com/Freed-Wu/python-wakatime

README.md

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -36,33 +36,59 @@
3636
[![pypi/implementation](https://shields.io/pypi/implementation/python-wakatime)](https://pypi.org/project/python-wakatime/#files)
3737
[![pypi/pyversions](https://shields.io/pypi/pyversions/python-wakatime)](https://pypi.org/project/python-wakatime/#files)
3838

39-
[wakatime](https://wakatime.com) plugin for python.
39+
[wakatime](https://wakatime.com) plugin for python REPLs.
4040

41-
## Usage
41+
Supported REPLs:
4242

43-
Add the following code to your
44-
[`$PYTHON_STARTUP`](https://docs.python.org/3/using/cmdline.html#envvar-PYTHONSTARTUP):
43+
- [x] [python](https://github.com/python/cpython):
44+
- executes
45+
[`str(sys.ps1)`](https://docs.python.org/3/library/sys.html#sys.ps1) after
46+
every input.
47+
- configure file:
48+
[`$PYTHON_STARTUP`](https://docs.python.org/3/using/cmdline.html#envvar-PYTHONSTARTUP).
4549

4650
```python
47-
from python_wakatime import install_hook
51+
from python_wakatime.python import install_hook
4852

49-
# ... # customize your PS1
50-
51-
# must after customization of PS1, best at the end of file
5253
install_hook()
5354
```
5455

55-
## FAQ
56+
- [x] [ptpython](https://github.com/prompt-toolkit/ptpython):
57+
- executes `get_ptpython().get_output_prompt()` after every output.
58+
- configure file: `.../ptpython/config.py`. `...` depends on OS.
59+
60+
```python
61+
from ptpython.repl import PythonRepl
62+
from python_wakatime.ptpython import install_hook
63+
64+
65+
def configure(repl: PythonRepl) -> None:
66+
install_hook(repl)
67+
```
68+
69+
- [x] [ipython](https://github.com/ipython/ipython):
70+
- executes
71+
`c.TerminalInteractiveShell.prompts_class(shell).out_prompt_tokens()` after
72+
every output.
73+
- configure file: `~/.ipython/profile_default/ipython_config.py`.
74+
75+
```python
76+
from python_wakatime.iptpython import install_hook
77+
78+
install_hook(c)
79+
```
5680

57-
Q: How does it work?
81+
- [x] [ptipython](https://github.com/prompt-toolkit/ptpython): Same as
82+
[ipython](https://github.com/ipython/ipython).
83+
- [ ] [bpython](https://github.com/bpython/bpython)
84+
- [ ] [xonsh](https://github.com/xonsh/xonsh)
85+
- [ ] [mypython](https://github.com/asmeurer/mypython): Won't fix.
86+
- configure file: non-exist.
5887

59-
A: Python executes
60-
[`str(sys.ps1)`](https://docs.python.org/3/library/sys.html#sys.ps1) after
61-
every inputting of the user. We can do anything what we want in
62-
`sys.ps1.__str__()`.
88+
`install_hook()` must after customization of prompt string, best at the end of file.
6389

6490
## Similar projects
6591

66-
- <https://wakatime.com/terminal> lists wakatime plugins for bash/zsh/fish.
92+
- <https://wakatime.com/terminal> lists wakatime plugins for many shells.
6793

6894
See more in [document](https://python-wakatime.readthedocs.io).

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
55
# https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html
66
[project]
77
name = "python-wakatime"
8-
description = "wakatime plugin for python"
8+
description = "wakatime plugin for python REPLs"
99
authors = [{ name = "Wu Zhenyu", email = "[email protected]" }]
1010
readme = "README.md"
1111
requires-python = ">= 3.9"

requirements/dev.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
# For unit test and code coverage rate test.
33

44
pre-commit
5+
ptpython[ptipython]

requirements/ipython.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/env -S pip install -r
2+
3+
ipython

requirements/ptipython.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/env -S pip install -r
2+
3+
ptpython[ptipython]

requirements/ptpython.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/env -S pip install -r
2+
3+
ptpython

src/python_wakatime/__init__.py

Lines changed: 3 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@
44
`create-plugin <https://wakatime.com/help/creating-plugin>`_
55
"""
66
import os
7-
import sys
87
from subprocess import run # nosec: B404
98
from threading import Thread
10-
from typing import Callable
9+
from typing import Any
1110

1211
from ._version import __version__, __version_tuple__ # type: ignore
1312

@@ -33,68 +32,13 @@ def send_wakatime_heartbeat(project: str = "Terminal") -> None:
3332
)
3433

3534

36-
def wakatime_hook(args: tuple = ()) -> None:
35+
def wakatime_hook(args: tuple = (), kwargs: dict[str, Any] = {}) -> None:
3736
"""Wakatime hook.
3837
3938
:param args:
4039
:type args: tuple
4140
:rtype: None
4241
"""
43-
task = Thread(target=send_wakatime_heartbeat, args=args)
42+
task = Thread(target=send_wakatime_heartbeat, args=args, kwargs=kwargs)
4443
task.daemon = True
4544
task.start()
46-
47-
48-
class Ps1:
49-
"""Ps1."""
50-
51-
def __init__(
52-
self,
53-
ps1: object = None,
54-
hook: Callable = wakatime_hook,
55-
args: tuple = (),
56-
) -> None:
57-
"""Init.
58-
59-
:param ps1:
60-
:type ps1: object
61-
:param hook:
62-
:type hook: Callable
63-
:param args:
64-
:type args: tuple
65-
:rtype: None
66-
"""
67-
if ps1:
68-
self.ps1 = ps1
69-
else:
70-
if hasattr(sys, "ps1"):
71-
self.ps1 = sys.ps1
72-
else:
73-
self.ps1 = ">>> "
74-
self.args = args
75-
self.hook = hook
76-
77-
def __str__(self) -> str:
78-
"""Str.
79-
80-
:rtype: str
81-
"""
82-
self.hook(*self.args)
83-
if isinstance(self.ps1, str):
84-
return self.ps1
85-
else:
86-
return str(self.ps1)
87-
88-
89-
def install_hook(hook: Callable = wakatime_hook, args: tuple = ()) -> None:
90-
"""Install hook. Only install once.
91-
92-
:param hook:
93-
:type hook: Callable
94-
:param args:
95-
:type args: tuple
96-
:rtype: None
97-
"""
98-
if hasattr(sys, "ps1") and isinstance(sys.ps1, Ps1):
99-
return
100-
sys.ps1 = Ps1(hook=hook, args=args)

src/python_wakatime/ipython.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
"""ipython
2+
==========
3+
"""
4+
from typing import Any, Callable
5+
6+
from IPython.terminal.interactiveshell import TerminalInteractiveShell
7+
from IPython.terminal.prompts import ClassicPrompts, Prompts
8+
from pygments.token import _TokenType
9+
from traitlets.config.loader import Config, LazyConfigValue
10+
11+
from . import wakatime_hook
12+
13+
14+
def get_new_prompts_class(
15+
prompts_class: type,
16+
hook: Callable = wakatime_hook,
17+
args: tuple = (),
18+
kwargs: dict[str, Any] = {},
19+
) -> type:
20+
"""Get new prompts class.
21+
22+
:param prompts_class:
23+
:type prompts_class: type
24+
:param hook:
25+
:type hook: Callable
26+
:param args:
27+
:type args: tuple
28+
:param kwargs:
29+
:type kwargs: dict[str, Any]
30+
:rtype: type
31+
"""
32+
if isinstance(prompts_class, LazyConfigValue):
33+
prompts_class = ClassicPrompts
34+
shell = TerminalInteractiveShell()
35+
36+
class Ps(Prompts):
37+
"""Ps."""
38+
39+
def in_prompt_tokens(self) -> list[tuple[_TokenType, str]]:
40+
"""In prompt tokens.
41+
42+
:rtype: list[tuple[_TokenType, str]]
43+
"""
44+
return prompts_class(shell).in_prompt_tokens()
45+
46+
def continuation_prompt_tokens(
47+
self, width: int | None = None
48+
) -> list[tuple[_TokenType, str]]:
49+
"""Continuation prompt tokens.
50+
51+
:param width:
52+
:type width: int | None
53+
:rtype: list[tuple[_TokenType, str]]
54+
"""
55+
return prompts_class(shell).continuation_prompt_tokens(width)
56+
57+
def rewrite_prompt_tokens(self) -> list[tuple[_TokenType, str]]:
58+
"""Rewrite prompt tokens.
59+
60+
:rtype: list[tuple[_TokenType, str]]
61+
"""
62+
return prompts_class(shell).rewrite_prompt_tokens()
63+
64+
def out_prompt_tokens(self) -> list[tuple[_TokenType, str]]:
65+
"""Out prompt tokens.
66+
67+
:rtype: list[tuple[_TokenType, str]]
68+
"""
69+
hook(*args, **kwargs)
70+
return prompts_class(shell).out_prompt_tokens()
71+
72+
return Ps
73+
74+
75+
def install_hook(
76+
c: Config,
77+
hook: Callable = wakatime_hook,
78+
args: tuple = (),
79+
kwargs: dict[str, Any] = {},
80+
) -> Config:
81+
"""Install hook.
82+
83+
:param c:
84+
:type c: Config
85+
:param hook:
86+
:type hook: Callable
87+
:param args:
88+
:type args: tuple
89+
:param kwargs:
90+
:type kwargs: dict[str, Any]
91+
:rtype: Config
92+
"""
93+
c.TerminalInteractiveShell.prompts_class = get_new_prompts_class( # type: ignore
94+
c.TerminalInteractiveShell.prompts_class, hook, args, kwargs # type: ignore
95+
)
96+
return c

0 commit comments

Comments
 (0)