Skip to content

Basic doctest #394

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Aug 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ $ pip install --user --upgrade --pre libtmux

- _Insert changes/features/fixes for next release here_

### Tests and docs

- Initial [doctests] examples stubbed out {issue}`#394`

[doctests]: https://docs.python.org/3/library/doctest.html

- Fix bug in `temp_window()` context manager, {issue}`#394`
- Pytest configuration `conftest.py` moved to `libtmux/conftest.py`, so doctest can
detect the fixtures {issue}`#394`

## libtmux 0.13.0 (2022-08-05)

### What's new
Expand Down
4 changes: 2 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@
from libtmux import test # NOQA

# Get the project root dir, which is the parent dir of this
cwd = Path.cwd()
cwd = Path(__file__).parent
project_root = cwd.parent

sys.path.insert(0, str(project_root))
sys.path.insert(0, str(cwd / "_ext"))

# package data
about: Dict[str, str] = {}
with open("../libtmux/__about__.py") as fp:
with open(project_root / "libtmux" / "__about__.py") as fp:
exec(fp.read(), about)

extensions = [
Expand Down
119 changes: 119 additions & 0 deletions libtmux/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import logging
import os
import typing as t

import pytest

from _pytest.fixtures import SubRequest
from _pytest.monkeypatch import MonkeyPatch

from libtmux import exc
from libtmux.common import which
from libtmux.server import Server
from libtmux.test import TEST_SESSION_PREFIX, get_test_session_name, namer

if t.TYPE_CHECKING:
from libtmux.session import Session

logger = logging.getLogger(__name__)


@pytest.fixture(autouse=True)
def clear_env(monkeypatch: MonkeyPatch) -> None:
"""Clear out any unnecessary environment variables that could interrupt tests.

tmux show-environment tests were being interrupted due to a lot of crazy env vars.
"""
for k, v in os.environ.items():
if not any(
needle in k.lower()
for needle in [
"window",
"tmux",
"pane",
"session",
"pytest",
"path",
"pwd",
"shell",
"home",
"xdg",
"disable_auto_title",
"lang",
"term",
]
):
monkeypatch.delenv(k)


@pytest.fixture(scope="function")
def server(request: SubRequest, monkeypatch: MonkeyPatch) -> Server:

t = Server()
t.socket_name = "tmuxp_test%s" % next(namer)

def fin() -> None:
t.kill_server()

request.addfinalizer(fin)

return t


@pytest.fixture(scope="function")
def session(request: SubRequest, server: Server) -> "Session":
session_name = "tmuxp"

if not server.has_session(session_name):
server.cmd("new-session", "-d", "-s", session_name)

# find current sessions prefixed with tmuxp
old_test_sessions = []
for s in server._sessions:
old_name = s.get("session_name")
if old_name is not None and old_name.startswith(TEST_SESSION_PREFIX):
old_test_sessions.append(old_name)

TEST_SESSION_NAME = get_test_session_name(server=server)

try:
session = server.new_session(session_name=TEST_SESSION_NAME)
except exc.LibTmuxException as e:
raise e

"""
Make sure that tmuxp can :ref:`test_builder_visually` and switches to
the newly created session for that testcase.
"""
session_id = session.get("session_id")
assert session_id is not None

try:
server.switch_client(target_session=session_id)
except exc.LibTmuxException:
# server.attach_session(session.get('session_id'))
pass

for old_test_session in old_test_sessions:
logger.debug("Old test test session %s found. Killing it." % old_test_session)
server.kill_session(old_test_session)
assert TEST_SESSION_NAME == session.get("session_name")
assert TEST_SESSION_NAME != "tmuxp"

return session


@pytest.fixture(autouse=True)
def add_doctest_fixtures(
doctest_namespace: t.Dict[str, t.Any],
# usefixtures / autouse
clear_env: t.Any,
# Normal fixtures
server: "Server",
session: "Session",
) -> None:
if which("tmux"):
doctest_namespace["server"] = server
doctest_namespace["session"] = session
doctest_namespace["window"] = session.attached_window
doctest_namespace["pane"] = session.attached_pane
33 changes: 31 additions & 2 deletions libtmux/pane.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,20 @@ class Pane(TmuxMappingObject):
----------
window : :class:`Window`

Examples
--------
>>> pane
Pane(%1 Window(@1 ...:..., Session($1 ...)))

>>> pane in window.panes
True

>>> pane.window
Window(@1 ...:..., Session($1 ...))

>>> pane.session
Session($1 ...)

Notes
-----

Expand Down Expand Up @@ -119,8 +133,7 @@ def send_keys(
suppress_history: t.Optional[bool] = True,
literal: t.Optional[bool] = False,
) -> None:
"""
``$ tmux send-keys`` to the pane.
r"""``$ tmux send-keys`` to the pane.

A leading space character is added to cmd to avoid polluting the
user's history.
Expand All @@ -135,6 +148,22 @@ def send_keys(
Don't add these keys to the shell history, default True.
literal : bool, optional
Send keys literally, default True.

Examples
--------
>>> pane = window.split_window(shell='sh')
>>> pane.capture_pane()
['$']

>>> pane.send_keys('echo "Hello world"', suppress_history=False, enter=True)

>>> pane.capture_pane()
['$ echo "Hello world"', 'Hello world', '$']

>>> print('\n'.join(pane.capture_pane())) # doctest: +NORMALIZE_WHITESPACE
$ echo "Hello world"
Hello world
$
"""
prefix = " " if suppress_history else ""

Expand Down
17 changes: 17 additions & 0 deletions libtmux/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,23 @@ class Server(TmuxRelationalObject["Session", "SessionDict"], EnvironmentMixin):
config_file : str, optional
colors : str, optional

Examples
--------
>>> server
<libtmux.server.Server object at ...>

>>> server.sessions
[Session($1 ...)]

>>> server.sessions[0].windows
[Window(@1 ...:..., Session($1 ...)]

>>> server.sessions[0].attached_window
Window(@1 ...:..., Session($1 ...)

>>> server.sessions[0].attached_pane
Pane(%1 Window(@1 ...:..., Session($1 ...)))

References
----------
.. [server_manual] CLIENTS AND SESSIONS. openbsd manpage for TMUX(1)
Expand Down
14 changes: 14 additions & 0 deletions libtmux/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,20 @@ class Session(
----------
server : :class:`Server`

Examples
--------
>>> session
Session($1 ...)

>>> session.windows
[Window(@1 ...:..., Session($1 ...)]

>>> session.attached_window
Window(@1 ...:..., Session($1 ...)

>>> session.attached_pane
Pane(%1 Window(@1 ...:..., Session($1 ...)))

References
----------
.. [session_manual] tmux session. openbsd manpage for TMUX(1).
Expand Down
27 changes: 18 additions & 9 deletions libtmux/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

if t.TYPE_CHECKING:
from libtmux.session import Session
from libtmux.window import Window

TEST_SESSION_PREFIX = "libtmux_"
RETRY_TIMEOUT_SECONDS = int(os.getenv("RETRY_TIMEOUT_SECONDS", 8))
Expand Down Expand Up @@ -68,16 +69,17 @@ def retry_until(
Examples
--------

>>> def f():
... p = w.attached_pane
>>> def fn():
... p = session.attached_window.attached_pane
... p.server._update_panes()
... return p.current_path == pane_path
...
... retry(f)
... return p.current_path is not None

>>> retry_until(fn)
True

In pytest:

>>> assert retry(f, raises=False)
>>> assert retry_until(fn, raises=False)
"""
ini = time.time()

Expand Down Expand Up @@ -179,6 +181,7 @@ def temp_session(

>>> with temp_session(server) as session:
... session.new_window(window_name='my window')
Window(@... ...:..., Session($... ...))
"""

if "session_name" in kwargs:
Expand All @@ -199,7 +202,7 @@ def temp_session(
@contextlib.contextmanager
def temp_window(
session: "Session", *args: t.Any, **kwargs: t.Any
) -> t.Generator["Session", t.Any, t.Any]:
) -> t.Generator["Window", t.Any, t.Any]:
"""
Return a context manager with a temporary window.

Expand Down Expand Up @@ -229,7 +232,13 @@ def temp_window(
--------

>>> with temp_window(session) as window:
... my_pane = window.split_window()
... window
Window(@... ...:..., Session($... ...))


>>> with temp_window(session) as window:
... window.split_window()
Pane(%... Window(@... ...:..., Session($... ...)))
"""

if "window_name" not in kwargs:
Expand All @@ -245,7 +254,7 @@ def temp_window(
assert isinstance(window_id, str)

try:
yield session
yield window
finally:
if session.find_where({"window_id": window_id}):
window.kill_window()
Expand Down
37 changes: 37 additions & 0 deletions libtmux/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,32 @@ class Window(TmuxMappingObject, TmuxRelationalObject["Pane", "PaneDict"]):
----------
session : :class:`Session`

Examples
--------
>>> window = session.new_window('My project')

>>> window
Window(@... ...:My project, Session($... ...))

Windows have panes:

>>> window.panes
[Pane(...)]

>>> window.attached_pane
Pane(...)

Relations moving up:

>>> window.session
Session(...)

>>> window == session.attached_window
True

>>> window in session.windows
True

References
----------
.. [window_manual] tmux window. openbsd manpage for TMUX(1).
Expand Down Expand Up @@ -296,6 +322,17 @@ def rename_window(self, new_name: str) -> "Window":
----------
new_name : str
name of the window

Examples
--------

>>> window = session.attached_window

>>> window.rename_window('My project')
Window(@1 ...:My project, Session($1 ...))

>>> window.rename_window('New name')
Window(@1 ...:New name, Session($1 ...))
"""

import shlex
Expand Down
3 changes: 2 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ line_length = 88
[tool:pytest]
filterwarnings =
ignore:.* Use packaging.version.*:DeprecationWarning::

addopts = --tb=short --no-header --showlocals --doctest-modules
doctest_optionflags = ELLIPSIS NORMALIZE_WHITESPACE
Loading