klab-pytest-toolkit-prompt 0.0.0__tar.gz

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,177 @@
1
+ # Created by https://www.toptal.com/developers/gitignore/api/python
2
+ # Edit at https://www.toptal.com/developers/gitignore?templates=python
3
+
4
+ ### Custom ###
5
+ pytest.xml
6
+ pytest-coverage.txt
7
+
8
+ ### Python ###
9
+ # Byte-compiled / optimized / DLL files
10
+ __pycache__/
11
+ *.py[cod]
12
+ *$py.class
13
+
14
+ # Distribution / packaging
15
+ .Python
16
+ build/
17
+ develop-eggs/
18
+ dist/
19
+ downloads/
20
+ eggs/
21
+ .eggs/
22
+ lib/
23
+ lib64/
24
+ parts/
25
+ sdist/
26
+ var/
27
+ wheels/
28
+ share/python-wheels/
29
+ *.egg-info/
30
+ .installed.cfg
31
+ *.egg
32
+ MANIFEST
33
+
34
+ # PyInstaller
35
+ # Usually these files are written by a python script from a template
36
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
37
+ *.manifest
38
+ *.spec
39
+
40
+ # Installer logs
41
+ pip-log.txt
42
+ pip-delete-this-directory.txt
43
+
44
+ # Unit test / coverage reports
45
+ htmlcov/
46
+ .tox/
47
+ .nox/
48
+ .coverage
49
+ .coverage.*
50
+ .cache
51
+ nosetests.xml
52
+ coverage.xml
53
+ *.cover
54
+ *.py,cover
55
+ .hypothesis/
56
+ .pytest_cache/
57
+ cover/
58
+
59
+ # Translations
60
+ *.mo
61
+ *.pot
62
+
63
+ # Django stuff:
64
+ *.log
65
+ local_settings.py
66
+ db.sqlite3
67
+ db.sqlite3-journal
68
+
69
+ # Flask stuff:
70
+ instance/
71
+ .webassets-cache
72
+
73
+ # Scrapy stuff:
74
+ .scrapy
75
+
76
+ # Sphinx documentation
77
+ docs/_build/
78
+
79
+ # PyBuilder
80
+ .pybuilder/
81
+ target/
82
+
83
+ # Jupyter Notebook
84
+ .ipynb_checkpoints
85
+
86
+ # IPython
87
+ profile_default/
88
+ ipython_config.py
89
+
90
+ # pyenv
91
+ # For a library or package, you might want to ignore these files since the code is
92
+ # intended to run in multiple environments; otherwise, check them in:
93
+ # .python-version
94
+
95
+ # pipenv
96
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
97
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
98
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
99
+ # install all needed dependencies.
100
+ #Pipfile.lock
101
+
102
+ # poetry
103
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
104
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
105
+ # commonly ignored for libraries.
106
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
107
+ #poetry.lock
108
+
109
+ # pdm
110
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
111
+ #pdm.lock
112
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
113
+ # in version control.
114
+ # https://pdm.fming.dev/#use-with-ide
115
+ .pdm.toml
116
+
117
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
118
+ __pypackages__/
119
+
120
+ # Celery stuff
121
+ celerybeat-schedule
122
+ celerybeat.pid
123
+
124
+ # SageMath parsed files
125
+ *.sage.py
126
+
127
+ # Environments
128
+ .env
129
+ .venv
130
+ env/
131
+ venv/
132
+ ENV/
133
+ env.bak/
134
+ venv.bak/
135
+
136
+ # Spyder project settings
137
+ .spyderproject
138
+ .spyproject
139
+
140
+ # Rope project settings
141
+ .ropeproject
142
+
143
+ # mkdocs documentation
144
+ /site
145
+
146
+ # mypy
147
+ .mypy_cache/
148
+ .dmypy.json
149
+ dmypy.json
150
+
151
+ # Pyre type checker
152
+ .pyre/
153
+
154
+ # pytype static type analyzer
155
+ .pytype/
156
+
157
+ # Cython debug symbols
158
+ cython_debug/
159
+
160
+ # PyCharm
161
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
162
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
163
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
164
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
165
+ #.idea/
166
+
167
+ ### Python Patch ###
168
+ # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
169
+ poetry.toml
170
+
171
+ # ruff
172
+ .ruff_cache/
173
+
174
+ # LSP config files
175
+ pyrightconfig.json
176
+
177
+ # End of https://www.toptal.com/developers/gitignore/api/python
@@ -0,0 +1,96 @@
1
+ Metadata-Version: 2.4
2
+ Name: klab-pytest-toolkit-prompt
3
+ Version: 0.0.0
4
+ Summary: Pytest prompt fixtures for the Klab Pytest Toolkit
5
+ Author-email: Burak Kizilkaya <burak.kizilkaya@outlook.com>
6
+ License-Expression: MIT
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Requires-Python: <4,>=3.11
12
+ Requires-Dist: pytest>=8.3.5
13
+ Description-Content-Type: text/markdown
14
+
15
+ # Klab Pytest Toolkit - Prompt
16
+
17
+ Custom pytest fixtures for interactive user prompts during test execution using tkinter UI dialogs.
18
+ The goal is to allow testers to interact with the test process, providing confirmations or displaying important information.
19
+
20
+ At the moment the package provides the following fixtures:
21
+
22
+ - `ui_prompt_factory`: Factory for creating multiple prompt instance. With the factory you can create the prompt fixture.
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ pip install klab-pytest-toolkit-prompt
28
+ ```
29
+
30
+ ## Usage
31
+
32
+ ### UI Prompt
33
+
34
+ **Create the fixture**
35
+
36
+ The factory class `PromptFactory` can be used to create multiple prompt instances.
37
+ This is already provided as a pytest fixture `prompt_factory`.
38
+
39
+ ```python
40
+
41
+ @pytest.fixture
42
+ def ui_prompt(prompt_factory) -> PromptInterface:
43
+ """Fixture to provide a UI prompt interface for user interaction during tests."""
44
+ return prompt_factory.create_prompt(prompt_type=PromptFactory.PromptType.UI_PROMPT)
45
+ ```
46
+
47
+ **Functions**
48
+
49
+ The following functions are available on the `PromptInterface` instance:
50
+
51
+ **Show Information Dialog**
52
+
53
+ ```python
54
+ def test_with_info(ui_prompt):
55
+ """Display information to the user."""
56
+ ui_prompt.show_info("Test is about to perform a critical operation")
57
+
58
+ # Continue with test
59
+ perform_operation()
60
+ ```
61
+
62
+ **Get User Confirmation**
63
+
64
+ ```python
65
+ def test_with_confirmation(ui_prompt):
66
+ """Get user confirmation before proceeding."""
67
+ if ui_prompt.confirm_action("Continue with destructive test?"):
68
+ # User clicked Yes
69
+ perform_destructive_operation()
70
+ else:
71
+ # User clicked No
72
+ pytest.skip("User cancelled the test")
73
+ ```
74
+
75
+ **With Timeout (Auto-close)**
76
+
77
+ ```python
78
+ def test_with_timeout(ui_prompt):
79
+ """Show dialog that auto-closes after timeout."""
80
+ # Dialog closes automatically after 5 seconds
81
+ ui_prompt.show_info("This will auto-close in 5 seconds", timeout=5)
82
+
83
+ # Confirmation dialog with timeout (returns False if timeout expires)
84
+ result = ui_prompt.confirm_action(
85
+ "Click within 10 seconds",
86
+ timeout=10
87
+ )
88
+ ```
89
+
90
+ ## Examples
91
+
92
+ See the `tests` directory for example test cases demonstrating the usage of the prompt fixtures.
93
+
94
+ ## License
95
+
96
+ MIT
@@ -0,0 +1,82 @@
1
+ # Klab Pytest Toolkit - Prompt
2
+
3
+ Custom pytest fixtures for interactive user prompts during test execution using tkinter UI dialogs.
4
+ The goal is to allow testers to interact with the test process, providing confirmations or displaying important information.
5
+
6
+ At the moment the package provides the following fixtures:
7
+
8
+ - `ui_prompt_factory`: Factory for creating multiple prompt instance. With the factory you can create the prompt fixture.
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ pip install klab-pytest-toolkit-prompt
14
+ ```
15
+
16
+ ## Usage
17
+
18
+ ### UI Prompt
19
+
20
+ **Create the fixture**
21
+
22
+ The factory class `PromptFactory` can be used to create multiple prompt instances.
23
+ This is already provided as a pytest fixture `prompt_factory`.
24
+
25
+ ```python
26
+
27
+ @pytest.fixture
28
+ def ui_prompt(prompt_factory) -> PromptInterface:
29
+ """Fixture to provide a UI prompt interface for user interaction during tests."""
30
+ return prompt_factory.create_prompt(prompt_type=PromptFactory.PromptType.UI_PROMPT)
31
+ ```
32
+
33
+ **Functions**
34
+
35
+ The following functions are available on the `PromptInterface` instance:
36
+
37
+ **Show Information Dialog**
38
+
39
+ ```python
40
+ def test_with_info(ui_prompt):
41
+ """Display information to the user."""
42
+ ui_prompt.show_info("Test is about to perform a critical operation")
43
+
44
+ # Continue with test
45
+ perform_operation()
46
+ ```
47
+
48
+ **Get User Confirmation**
49
+
50
+ ```python
51
+ def test_with_confirmation(ui_prompt):
52
+ """Get user confirmation before proceeding."""
53
+ if ui_prompt.confirm_action("Continue with destructive test?"):
54
+ # User clicked Yes
55
+ perform_destructive_operation()
56
+ else:
57
+ # User clicked No
58
+ pytest.skip("User cancelled the test")
59
+ ```
60
+
61
+ **With Timeout (Auto-close)**
62
+
63
+ ```python
64
+ def test_with_timeout(ui_prompt):
65
+ """Show dialog that auto-closes after timeout."""
66
+ # Dialog closes automatically after 5 seconds
67
+ ui_prompt.show_info("This will auto-close in 5 seconds", timeout=5)
68
+
69
+ # Confirmation dialog with timeout (returns False if timeout expires)
70
+ result = ui_prompt.confirm_action(
71
+ "Click within 10 seconds",
72
+ timeout=10
73
+ )
74
+ ```
75
+
76
+ ## Examples
77
+
78
+ See the `tests` directory for example test cases demonstrating the usage of the prompt fixtures.
79
+
80
+ ## License
81
+
82
+ MIT
@@ -0,0 +1,39 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "klab-pytest-toolkit-prompt"
7
+ dynamic = ["version"]
8
+ description = "Pytest prompt fixtures for the Klab Pytest Toolkit"
9
+ authors = [{ name = "Burak Kizilkaya", email = "burak.kizilkaya@outlook.com" }]
10
+ requires-python = ">=3.11,<4"
11
+ readme = "README.md"
12
+ license = "MIT"
13
+ classifiers = [
14
+ "Development Status :: 3 - Alpha",
15
+ "Intended Audience :: Developers",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Programming Language :: Python :: 3",
18
+ ]
19
+ dependencies = [
20
+ "pytest>=8.3.5",
21
+ ]
22
+
23
+ [project.entry-points.pytest11]
24
+ klab_pytest_toolkit_prompt = "klab_pytest_toolkit_prompt.plugin"
25
+
26
+ [tool.hatch.version]
27
+ path = "src/klab_pytest_toolkit_prompt/__init__.py"
28
+
29
+ [tool.hatch.build.targets.sdist]
30
+ exclude = [
31
+ "tests",
32
+ ]
33
+
34
+ [tool.pytest.ini_options]
35
+ asyncio_mode = "auto"
36
+ asyncio_default_fixture_loop_scope = "function"
37
+ pythonpath = [
38
+ "."
39
+ ]
@@ -0,0 +1,10 @@
1
+ """Klab Pytest Toolkit - Prompt"""
2
+
3
+ from klab_pytest_toolkit_prompt.core import PromptInterface, PromptFactory
4
+
5
+ __version__ = "0.0.0"
6
+
7
+ __all__ = [
8
+ "PromptInterface",
9
+ "PromptFactory",
10
+ ]
@@ -0,0 +1,45 @@
1
+ import abc
2
+ from typing import Optional
3
+
4
+
5
+ class PromptInterface(abc.ABC):
6
+ """Abstract base class for prompt interfaces."""
7
+
8
+ @abc.abstractmethod
9
+ def show_info(self, message: str, timeout: Optional[int] = None) -> None:
10
+ """Display an informational message to the user."""
11
+ raise NotImplementedError
12
+
13
+ def confirm_action(self, message: str, timeout: Optional[int] = None) -> Optional[bool]:
14
+ """Prompt the user to confirm an action."""
15
+ raise NotImplementedError
16
+
17
+ def get_user_input(self, prompt: str, timeout: Optional[int] = None) -> Optional[str]:
18
+ """Prompt the user for input."""
19
+ raise NotImplementedError
20
+
21
+ def __enter__(self):
22
+ """Enter the runtime context related to this object."""
23
+ return self
24
+
25
+ def __exit__(self, exc_type, exc_value, traceback):
26
+ """Exit the runtime context related to this object."""
27
+ pass
28
+
29
+
30
+ class PromptFactory:
31
+ """Factory for creating prompt interface instances."""
32
+
33
+ class PromptType:
34
+ UI_PROMPT = "ui_prompt"
35
+ # Future prompt types can be added here
36
+
37
+ @staticmethod
38
+ def create_prompt(prompt_type: str = PromptType.UI_PROMPT) -> PromptInterface:
39
+ """Create a prompt interface instance based on the specified type."""
40
+ if prompt_type == PromptFactory.PromptType.UI_PROMPT:
41
+ from klab_pytest_toolkit_prompt.ui import UiPrompt
42
+
43
+ return UiPrompt()
44
+ else:
45
+ raise ValueError(f"Unsupported prompt type: {prompt_type}")
@@ -0,0 +1,24 @@
1
+ """Pytest plugin to register prompt fixtures."""
2
+
3
+ from klab_pytest_toolkit_prompt.core import PromptFactory
4
+ import pytest
5
+
6
+
7
+ def pytest_configure(config):
8
+ """Register custom markers and configure prompt fixtures."""
9
+ pass
10
+
11
+
12
+ def pytest_addoption(parser):
13
+ """Add command line and ini file options."""
14
+ pass
15
+
16
+
17
+ @pytest.fixture
18
+ def prompt_factory() -> PromptFactory:
19
+ """Factory fixture to create prompt interface instances.
20
+
21
+ Returns:
22
+ PromptFactory: An instance of the PromptFactory
23
+ """
24
+ return PromptFactory()
@@ -0,0 +1,116 @@
1
+ import tkinter as tk
2
+ from tkinter import messagebox, simpledialog
3
+ from typing import Any, Callable, Optional, TypeVar
4
+
5
+ from klab_pytest_toolkit_prompt.core import PromptInterface
6
+
7
+ T = TypeVar("T")
8
+
9
+
10
+ class UiPrompt(PromptInterface):
11
+ """Concrete implementation of PromptInterface for UI prompts.
12
+
13
+ The implementation will be done with tkinter, because it is included in the standard library.
14
+ """
15
+
16
+ def _run_dialog(
17
+ self,
18
+ dialog_fn: Callable[[tk.Tk], T],
19
+ timeout: Optional[int] = None,
20
+ default: Optional[T] = None,
21
+ ) -> Optional[T]:
22
+ """Run a dialog with optional timeout handling.
23
+
24
+ Args:
25
+ dialog_fn: Function that takes root window and returns dialog result
26
+ timeout: Optional timeout in seconds
27
+ default: Default value to return on timeout or error
28
+
29
+ Returns:
30
+ The dialog result or default value
31
+ """
32
+ result: Any = default
33
+ timeout_expired = False
34
+
35
+ def show():
36
+ nonlocal result, timeout_expired
37
+ root = tk.Tk()
38
+ root.withdraw()
39
+ root.update_idletasks()
40
+
41
+ if timeout:
42
+
43
+ def auto_close():
44
+ nonlocal timeout_expired
45
+ timeout_expired = True
46
+ self._safe_destroy(root)
47
+
48
+ root.after(timeout * 1000, auto_close)
49
+
50
+ try:
51
+ dialog_result = dialog_fn(root)
52
+ if not timeout_expired:
53
+ result = dialog_result
54
+ except tk.TclError:
55
+ pass
56
+
57
+ self._safe_destroy(root)
58
+
59
+ show()
60
+ return result
61
+
62
+ @staticmethod
63
+ def _safe_destroy(root: tk.Tk) -> None:
64
+ """Safely quit and destroy a tkinter root window."""
65
+ try:
66
+ root.quit()
67
+ root.destroy()
68
+ except tk.TclError:
69
+ pass
70
+
71
+ def show_info(self, message: str, timeout: Optional[int] = None) -> None:
72
+ """Display an informational message to the user.
73
+
74
+ Args:
75
+ message: The message to display
76
+ timeout: Optional timeout in seconds. If provided, the dialog will
77
+ automatically close after this time.
78
+ """
79
+ self._run_dialog(
80
+ lambda root: messagebox.showinfo("Information", message, parent=root),
81
+ timeout=timeout,
82
+ )
83
+
84
+ def confirm_action(self, message: str, timeout: Optional[int] = None) -> Optional[bool]:
85
+ """Prompt the user to confirm an action.
86
+
87
+ Args:
88
+ message: The confirmation message to display
89
+ timeout: Optional timeout in seconds. If provided and expires,
90
+ returns False (no confirmation).
91
+
92
+ Returns:
93
+ bool: True if user confirmed (clicked Yes/OK), False otherwise
94
+ """
95
+ return self._run_dialog(
96
+ lambda root: messagebox.askyesno("Confirm Action", message, parent=root),
97
+ timeout=timeout,
98
+ default=False,
99
+ )
100
+
101
+ def get_user_input(self, prompt: str, timeout: Optional[int] = None) -> Optional[str]:
102
+ """Prompt the user for input.
103
+
104
+ Args:
105
+ prompt: The prompt message to display
106
+ timeout: Optional timeout in seconds. If provided and expires,
107
+ returns None.
108
+
109
+ Returns:
110
+ Optional[str]: The user input, or None if cancelled or timed out
111
+ """
112
+ return self._run_dialog(
113
+ lambda root: simpledialog.askstring("Input Required", prompt, parent=root),
114
+ timeout=timeout,
115
+ default=None,
116
+ )