Skip to content

Commit a07abec

Browse files
authored
refactor: Add mypy to CI, type corrections, overhaul dev docs (#382)
You can run `make watch_mypy` now
2 parents 92acea6 + 7d72331 commit a07abec

File tree

9 files changed

+165
-39
lines changed

9 files changed

+165
-39
lines changed

.github/workflows/tests.yml

+5
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,14 @@ jobs:
8484
- name: Install python dependencies
8585
run: |
8686
poetry install -E "test coverage lint"
87+
8788
- name: Lint with flake8
8889
run: |
8990
poetry run flake8
91+
92+
- name: Lint with mypy
93+
run: poetry run mypy .
94+
9095
- name: Test with pytest
9196
continue-on-error: ${{ matrix.tmux-version == 'master' }}
9297
run: |

CHANGES

+6
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ $ pip install --user --upgrade --pre libtmux
3535
- `retry()`: Add deprecation warning. This will be removed in 0.13.x ({issue}`368`, {issue}`372`)
3636
- New function `retry_until()`: Polls a callback function for a set period of time until it returns `True` or times out. By default it will raise {exc}`libtmux.exc.WaitTimeout`, with `raises=False` it will return `False`. Thank you @categulario! ({issue}`368`, {issue}`372`)
3737

38+
### Internals
39+
40+
- {issue}`382` [mypy] support added:
41+
42+
- Basic mypy tests now pass
43+
3844
## libtmux 0.11.0 (2022-03-10)
3945

4046
### Compatibility

docs/conf.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import sys
55
from os.path import dirname, relpath
66
from pathlib import Path
7+
from typing import Dict, List
78

89
import libtmux # NOQA
910
from libtmux import test # NOQA
@@ -16,7 +17,7 @@
1617
sys.path.insert(0, str(cwd / "_ext"))
1718

1819
# package data
19-
about = {}
20+
about: Dict = {}
2021
with open("../libtmux/__about__.py") as fp:
2122
exec(fp.read(), about)
2223

@@ -68,8 +69,8 @@
6869
html_css_files = ["css/custom.css"]
6970
html_extra_path = ["manifest.json"]
7071
html_theme = "furo"
71-
html_theme_path = []
72-
html_theme_options = {
72+
html_theme_path: List = []
73+
html_theme_options: Dict = {
7374
"light_logo": "img/libtmux.svg",
7475
"dark_logo": "img/libtmux.svg",
7576
"footer_icons": [

docs/developing.md

+128-26
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,17 @@
22

33
[poetry] is a required package to develop.
44

5-
`git clone https://github.com/tmux-python/libtmux.git`
5+
```console
6+
$ git clone https://github.com/tmux-python/libtmux.git
7+
```
68

7-
`cd libtmux`
9+
```console
10+
$ cd libtmux
11+
```
812

9-
`poetry install -E "docs test coverage lint format"`
13+
```console
14+
$ poetry install -E "docs test coverage lint format"
15+
```
1016

1117
Makefile commands prefixed with `watch_` will watch files and rerun.
1218

@@ -21,23 +27,122 @@ Rerun tests on file change: `make watch_test` (requires [entr(1)])
2127

2228
Default preview server: http://localhost:8023
2329

30+
[sphinx-autobuild] will automatically build the docs, watch for file changes and launch a server.
31+
32+
From home directory: `make start_docs` From inside `docs/`: `make start`
33+
34+
[sphinx-autobuild]: https://github.com/executablebooks/sphinx-autobuild
35+
36+
### Manual documentation (the hard way)
37+
2438
`cd docs/` and `make html` to build. `make serve` to start http server.
2539

26-
Helpers:
27-
`make build_docs`, `make serve_docs`
40+
Helpers: `make build_docs`, `make serve_docs`
2841

2942
Rebuild docs on file change: `make watch_docs` (requires [entr(1)])
3043

31-
Rebuild docs and run server via one terminal: `make dev_docs` (requires above, and a
32-
`make(1)` with `-J` support, e.g. GNU Make)
44+
Rebuild docs and run server via one terminal: `make dev_docs` (requires above, and a `make(1)` with
45+
`-J` support, e.g. GNU Make)
46+
47+
## Formatting
48+
49+
The project uses [black] and [isort] (one after the other). Configurations are in `pyproject.toml`
50+
and `setup.cfg`:
51+
52+
- `make black isort`: Run `black` first, then `isort` to handle import nuances
53+
54+
## Linting
55+
56+
[flake8] and [mypy] run via CI in our GitHub Actions. See the configuration in `pyproject.toml` and
57+
`setup.cfg`.
58+
59+
### flake8
60+
61+
[flake8] provides fast, reliable, barebones styling and linting.
62+
63+
````{tab} Command
64+
65+
poetry:
66+
67+
```console
68+
$ poetry run flake8
69+
```
70+
71+
If you setup manually:
72+
73+
```console
74+
$ flake8
75+
```
76+
77+
````
78+
79+
````{tab} make
80+
81+
```console
82+
$ make flake8
83+
```
84+
85+
````
86+
87+
````{tab} Watch
88+
89+
```console
90+
$ make watch_flake8
91+
```
92+
93+
requires [`entr(1)`].
3394
34-
## Formatting / Linting
95+
````
3596

36-
The project uses [black] and [isort] (one after the other) and runs [flake8] via
37-
CI. See the configuration in `pyproject.toml` and `setup.cfg`:
97+
````{tab} Configuration
3898
39-
`make black isort`: Run `black` first, then `isort` to handle import nuances
40-
`make flake8`, to watch (requires `entr(1)`): `make watch_flake8`
99+
See `[flake8]` in setup.cfg.
100+
101+
```{literalinclude} ../setup.cfg
102+
:language: ini
103+
:start-at: "[flake8]"
104+
:end-before: "[isort]"
105+
106+
```
107+
108+
````
109+
110+
### mypy
111+
112+
[mypy] is used for static type checking.
113+
114+
````{tab} Command
115+
116+
poetry:
117+
118+
```console
119+
$ poetry run mypy .
120+
```
121+
122+
If you setup manually:
123+
124+
```console
125+
$ mypy .
126+
```
127+
128+
````
129+
130+
````{tab} make
131+
132+
```console
133+
$ make mypy
134+
```
135+
136+
````
137+
138+
````{tab} Watch
139+
140+
```console
141+
$ make watch_mypy
142+
```
143+
144+
requires [`entr(1)`].
145+
````
41146

42147
## Releasing
43148

@@ -68,23 +173,18 @@ the top::
68173

69174
`libtmux/__init__.py` and `__about__.py` - Set version
70175

71-
`git commit -m 'Tag v0.9.1'`
72-
73-
`git tag v0.9.1`
74-
75-
`pip install wheel twine`
76-
77-
`python setup.py sdist bdist_wheel`
78-
79-
`twine upload dist/*`
80-
81-
### Twine
176+
```console
177+
$ git commit -m 'Tag v0.9.1'
178+
```
82179

83-
`twine upload dist/*`
180+
```console
181+
$ git tag v0.9.1
182+
```
84183

85-
You will be asked for PyPI login information.
184+
After `git push` and `git push --tags`, CI will automatically build and deploy
185+
to PyPI.
86186

87-
### Releasing with Poetry (hypothetical)
187+
### Releasing with Poetry (manual)
88188

89189
This isn't used yet since package maintainers may want setup.py in the source.
90190
See https://github.com/tmux-python/tmuxp/issues/625.
@@ -104,6 +204,8 @@ Update `__version__` in `__about__.py` and `pyproject.toml`::
104204
[twine]: https://twine.readthedocs.io/
105205
[poetry]: https://python-poetry.org/
106206
[entr(1)]: http://eradman.com/entrproject/
207+
[`entr(1)`]: http://eradman.com/entrproject/
107208
[black]: https://github.com/psf/black
108209
[isort]: https://pypi.org/project/isort/
109210
[flake8]: https://flake8.pycqa.org/
211+
[mypy]: http://mypy-lang.org/

libtmux/_compat.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,4 @@ def str_from_console(s: t.Union[str, bytes]) -> str:
2424
try:
2525
return str(s)
2626
except UnicodeDecodeError:
27-
return str(s, encoding="utf_8")
27+
return str(s, encoding="utf_8") if isinstance(s, bytes) else s

libtmux/server.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ def new_session(
534534
if env:
535535
del os.environ["TMUX"]
536536

537-
tmux_args = (
537+
tmux_args: t.Tuple = (
538538
"-s%s" % session_name,
539539
"-P",
540540
"-F%s" % formats.FORMAT_SEPARATOR.join(tmux_formats), # output

libtmux/session.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ def new_window(
209209
wformats = ["session_name", "session_id"] + formats.WINDOW_FORMATS
210210
tmux_formats = ["#{%s}" % f for f in wformats]
211211

212-
window_args = tuple()
212+
window_args: t.Tuple = tuple()
213213

214214
if not attach:
215215
window_args += ("-d",)

libtmux/test.py

+16-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import contextlib
33
import logging
44
import os
5-
import tempfile
5+
import random
66
import time
77
import warnings
88
from typing import Callable, Optional
@@ -15,13 +15,25 @@
1515
RETRY_TIMEOUT_SECONDS = int(os.getenv("RETRY_TIMEOUT_SECONDS", 8))
1616
RETRY_INTERVAL_SECONDS = float(os.getenv("RETRY_INTERVAL_SECONDS", 0.05))
1717

18-
namer = tempfile._RandomNameSequence()
18+
19+
class RandomStrSequence:
20+
def __init__(self, characters: str = "abcdefghijklmnopqrstuvwxyz0123456789_"):
21+
self.characters: str = characters
22+
23+
def __iter__(self):
24+
return self
25+
26+
def __next__(self):
27+
return "".join(random.sample(self.characters, k=8))
28+
29+
30+
namer = RandomStrSequence()
1931
current_dir = os.path.abspath(os.path.dirname(__file__))
2032
example_dir = os.path.abspath(os.path.join(current_dir, "..", "examples"))
2133
fixtures_dir = os.path.realpath(os.path.join(current_dir, "fixtures"))
2234

2335

24-
def retry(seconds: Optional[float] = RETRY_TIMEOUT_SECONDS) -> bool:
36+
def retry(seconds: float = RETRY_TIMEOUT_SECONDS) -> bool:
2537
"""
2638
Retry a block of code until a time limit or ``break``.
2739
@@ -60,7 +72,7 @@ def retry_until(
6072
fun: Callable,
6173
seconds: float = RETRY_TIMEOUT_SECONDS,
6274
*,
63-
interval: Optional[float] = RETRY_INTERVAL_SECONDS,
75+
interval: float = RETRY_INTERVAL_SECONDS,
6476
raises: Optional[bool] = True,
6577
) -> bool:
6678
"""

libtmux/window.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,7 @@ def __repr__(self):
6969
)
7070

7171
@property
72-
def _info(self, *args):
73-
72+
def _info(self):
7473
attrs = {"window_id": self._window_id}
7574

7675
# from https://github.com/serkanyersen/underscore.py
@@ -445,7 +444,7 @@ def split_window(
445444

446445
# '-t%s' % self.attached_pane.get('pane_id'),
447446
# 2013-10-18 LOOK AT THIS, rm'd it..
448-
tmux_args = tuple()
447+
tmux_args: t.Tuple = tuple()
449448

450449
if target:
451450
tmux_args += ("-t%s" % target,)
@@ -504,6 +503,7 @@ def attached_pane(self) -> t.Optional[Pane]:
504503
# for now pane_active is a unicode
505504
if "pane_active" in pane and pane.get("pane_active") == "1":
506505
return Pane(window=self, **pane)
506+
return None
507507

508508
def _list_panes(self) -> t.List[PaneDict]:
509509
panes = self.server._update_panes()._panes

0 commit comments

Comments
 (0)