Skip to content

Commit fc4f695

Browse files
committed
✨ Support detecting project
Add some unit tests Fix the bug on args/kwargs Change the plugin names, see wakatime/wakatime-cli#805
1 parent 0d04fb2 commit fc4f695

File tree

8 files changed

+119
-11
lines changed

8 files changed

+119
-11
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ install_hook(c)
8585
- [ ] [mypython](https://github.com/asmeurer/mypython): Won't fix.
8686
- configure file: non-exist.
8787

88-
`install_hook()` must after customization of prompt string, best at the end of file.
88+
`install_hook()` must be after customization of prompt string, best at the end
89+
of file.
8990

9091
## Similar projects
9192

pyproject.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,13 @@ classifiers = [
2222
"Operating System :: MacOS",
2323
"Programming Language :: Python :: 3 :: Only",
2424
"Programming Language :: Python :: 3",
25+
"Programming Language :: Python :: 3.7",
26+
"Programming Language :: Python :: 3.8",
2527
"Programming Language :: Python :: 3.9",
2628
"Programming Language :: Python :: 3.10",
2729
"Programming Language :: Python :: 3.11",
30+
"Programming Language :: Python :: Implementation :: CPython",
31+
"Programming Language :: Python :: Implementation :: PyPy",
2832
]
2933
dynamic = ["version", "optional-dependencies"]
3034

src/python_wakatime/__init__.py

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,52 @@
11
"""Wakatime
22
===========
33
4-
`create-plugin <https://wakatime.com/help/creating-plugin>`_
4+
Refer `create-plugin <https://wakatime.com/help/creating-plugin>`_.
55
"""
6+
import logging
67
import os
8+
from shutil import which
79
from subprocess import run # nosec: B404
810
from threading import Thread
9-
from typing import Any
11+
from typing import Any, Callable
1012

1113
from ._version import __version__, __version_tuple__ # type: ignore
1214

15+
logger = logging.getLogger(__name__)
1316

14-
def send_wakatime_heartbeat(project: str = "Terminal") -> None:
17+
18+
def send_wakatime_heartbeat(
19+
project: str = "",
20+
plugin: str = "repl-python-wakatime",
21+
filenames: list[str] = [".git"],
22+
detect_func: Callable[[str], bool] = os.path.isdir,
23+
) -> None:
1524
"""Send wakatime heartbeat.
1625
26+
If ``project == ""``, detect automatically.
27+
28+
``plugin`` must be the format of ``repl-REPL_NAME-wakatime`` to let
29+
wakatime detect correctly.
30+
1731
:param project:
1832
:type project: str
33+
:param plugin:
34+
:type plugin: str
35+
:param filenames:
36+
:type filenames: list[str]
37+
:param detect_func:
38+
:type detect_func: Callable[[str], bool]
1939
:rtype: None
2040
"""
41+
if project == "":
42+
from .utils import get_project
43+
44+
project = get_project(filenames, detect_func)
2145
run( # nosec: B603 B607
2246
[
2347
"wakatime-cli",
2448
"--write",
25-
"--plugin=python-wakatime",
49+
f"--plugin={plugin}",
2650
"--entity-type=app",
2751
"--entity=python",
2852
"--alternate-language=python",
@@ -39,6 +63,9 @@ def wakatime_hook(args: tuple = (), kwargs: dict[str, Any] = {}) -> None:
3963
:type args: tuple
4064
:rtype: None
4165
"""
66+
if which("wakatime-cli") is None:
67+
logger.error("Please install wakatime-cli firstly!")
68+
return
4269
task = Thread(target=send_wakatime_heartbeat, args=args, kwargs=kwargs)
4370
task.daemon = True
4471
task.start()

src/python_wakatime/ipython.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def out_prompt_tokens(self) -> list[tuple[_TokenType, str]]:
6666
6767
:rtype: list[tuple[_TokenType, str]]
6868
"""
69-
hook(*args, **kwargs)
69+
hook(args, kwargs)
7070
return prompts_class(shell).out_prompt_tokens()
7171

7272
return Ps
@@ -76,7 +76,7 @@ def install_hook(
7676
c: Config,
7777
hook: Callable = wakatime_hook,
7878
args: tuple = (),
79-
kwargs: dict[str, Any] = {},
79+
kwargs: dict[str, Any] = {"plugin": "repl-ipython-wakatime"},
8080
) -> Config:
8181
"""Install hook.
8282

src/python_wakatime/ptpython.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,15 @@ def out_prompt(self) -> AnyFormattedText:
6060
6161
:rtype: AnyFormattedText
6262
"""
63-
self.hook(*self.args, **self.kwargs)
63+
self.hook(self.args, self.kwargs)
6464
return self.prompt_style.out_prompt()
6565

6666

6767
def install_hook(
6868
repl: PythonRepl,
6969
hook: Callable = wakatime_hook,
7070
args: tuple = (),
71-
kwargs: dict[str, Any] = {},
71+
kwargs: dict[str, Any] = {"plugin": "repl-ptpython-wakatime"},
7272
hook_prefix: str = "ps1_",
7373
) -> PythonRepl:
7474
"""Install hook.

src/python_wakatime/python.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def __str__(self) -> str:
4545
4646
:rtype: str
4747
"""
48-
self.hook(*self.args, **self.kwargs)
48+
self.hook(self.args, self.kwargs)
4949
if isinstance(self.ps1, str):
5050
return self.ps1
5151
else:
@@ -55,7 +55,7 @@ def __str__(self) -> str:
5555
def install_hook(
5656
hook: Callable = wakatime_hook,
5757
args: tuple = (),
58-
kwargs: dict[str, Any] = {},
58+
kwargs: dict[str, Any] = {"plugin": "repl-python-wakatime"},
5959
) -> object:
6060
"""Install hook.
6161

src/python_wakatime/utils.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"""Utils
2+
========
3+
"""
4+
import os
5+
from typing import Callable
6+
7+
8+
def get_project(
9+
filenames: list[str] = [".git"],
10+
detect_func: Callable[[str], bool] = os.path.isdir,
11+
) -> str:
12+
"""Get project.
13+
14+
If ``.git`` is a directory, use current directory. If not, detect its
15+
parent directory. If its parent directory is itself (which means it is the
16+
root directory) and ``.git`` is still not a directory, stop detection. Just
17+
use current directory as ``project``.
18+
19+
:param filenames:
20+
:type filenames: list[str]
21+
:param detect_func:
22+
:type detect_func: Callable[[str], bool]
23+
"""
24+
cwd = os.getcwd()
25+
project = cwd
26+
oldproject = ""
27+
while not any(
28+
detect_func(os.path.join(project, filename)) for filename in filenames
29+
):
30+
if oldproject == project:
31+
project = cwd
32+
break
33+
oldproject = project
34+
project = os.path.dirname(project)
35+
project = os.path.basename(project)
36+
return project

tests/__init___test.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""Test
2+
=======
3+
"""
4+
import os
5+
from pathlib import Path
6+
7+
from python_wakatime.utils import get_project
8+
9+
10+
class Test:
11+
"""Test."""
12+
13+
def test_get_project_no_cwd(self, tmp_path: Path) -> None:
14+
"""Test get project no cwd.
15+
16+
:param tmp_path:
17+
:type tmp_path: Path
18+
:rtype: None
19+
"""
20+
os.chdir(tmp_path)
21+
os.makedirs(".svn")
22+
os.makedirs("subdir")
23+
os.chdir("subdir")
24+
rst = get_project([".git", ".svn"])
25+
expected = os.path.basename(tmp_path)
26+
assert rst == expected
27+
28+
def test_get_project_cwd(self, tmp_path: Path) -> None:
29+
"""Test get project cwd.
30+
31+
:param tmp_path:
32+
:type tmp_path: Path
33+
:rtype: None
34+
"""
35+
os.chdir(tmp_path)
36+
os.makedirs("subdir")
37+
os.chdir("subdir")
38+
rst = get_project([".git", ".svn"])
39+
expected = "subdir"
40+
assert rst == expected

0 commit comments

Comments
 (0)