emtest 0.0.1__py3-none-any.whl → 0.0.3__py3-none-any.whl

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.

Potentially problematic release.


This version of emtest might be problematic. Click here for more details.

@@ -0,0 +1,34 @@
1
+ """Rerun the orignially executed python script in pytest instead of python."""
2
+ import os
3
+ import sys
4
+
5
+ from emtest import run_pytest
6
+
7
+ # Configuration for standalone execution
8
+ BREAKPOINTS = False # Set to True to enable debugger on test failures
9
+
10
+ path_parts = sys.argv[0].split(os.sep)
11
+ if not (
12
+ path_parts[-1] == "pytest"
13
+ or (
14
+ len(path_parts) > 1
15
+ and path_parts[-2] == "pytest"
16
+ and path_parts[-1] == "__main__.py"
17
+ )
18
+ ):
19
+ test_file = os.path.abspath(sys.argv[0])
20
+ pytest_args=sys.argv[1:]
21
+ # print("RERUNNING IN PYTEST:", test_file)
22
+
23
+ # Use emtest's custom test runner with specific settings:
24
+ run_pytest(
25
+ test_path=test_file, # Run tests in this file
26
+ breakpoints=BREAKPOINTS, # Enable/disable debugger on failures
27
+ enable_print=True, # Show print statements in tests
28
+ pytest_args=pytest_args
29
+ )
30
+
31
+ sys.exit(-1)
32
+ else:
33
+ # print("ALREADY RUNNING IN PYTEST")
34
+ pass
emtest/__init__.py CHANGED
@@ -1,7 +1,14 @@
1
- from .pytest_utils import run_pytest, configure_pytest_reporter
1
+ from .pytest_utils import run_pytest, configure_pytest_reporter, env_vars
2
2
  from .testing_utils import (
3
3
  add_path_to_python,
4
4
  assert_is_loaded_from_source,
5
5
  polite_wait,
6
6
  await_thread_cleanup,
7
- )
7
+ get_thread_names,
8
+ set_env_var,
9
+ are_we_in_docker,
10
+ make_dir,
11
+ delete_path,
12
+ ensure_dirs_exist,
13
+ ensure_dir_exists,
14
+ )
emtest/pytest_utils.py CHANGED
@@ -1,6 +1,6 @@
1
1
 
2
2
 
3
-
3
+ from .testing_utils import set_env_var
4
4
  import os
5
5
  import sys
6
6
  from typing import Any, Optional
@@ -9,24 +9,29 @@ from _pytest.config import Config
9
9
  from _pytest.reports import TestReport
10
10
  from termcolor import colored
11
11
 
12
+ from environs import Env
13
+ env_vars = Env()
14
+
15
+
12
16
  class MinimalReporter(TerminalReporter):
13
17
  """Custom pytest reporter that provides clean, minimal output with colored symbols.
14
-
18
+
15
19
  This reporter suppresses most default pytest output and displays only:
16
20
  - ✓ for passed tests (green)
17
21
  - ✗ for failed tests (red)
18
22
  - - for skipped tests (yellow)
19
23
  """
20
- def __init__(self, config: Config) -> None:
24
+
25
+ def __init__(self, config: Config, print_errors: bool = True) -> None:
21
26
  super().__init__(config)
22
27
  self._tw.hasmarkup = True # enables colored output safely
28
+ self.print_errors = print_errors
29
+ self.current_test_file = ""
23
30
 
24
- def _write_output(self, *args: Any, **kwargs: Any) -> None:
25
- """Override default output methods to suppress them."""
26
- pass # override all default output methods
31
+ def report_collect(self, final: bool = False) -> None:
32
+ pass
27
33
 
28
- def _write_summary(self) -> None:
29
- """Override summary writing to suppress it."""
34
+ def pytest_collection(self) -> None:
30
35
  pass
31
36
 
32
37
  def pytest_sessionstart(self, session: Any) -> None:
@@ -37,6 +42,10 @@ class MinimalReporter(TerminalReporter):
37
42
  def pytest_runtest_logstart(self, nodeid: str, location: Any) -> None:
38
43
  """Override test start logging to suppress it."""
39
44
  # print("pytest_runtest_logstart")
45
+ current_test_file = os.path.basename(location[0]).strip(".py")
46
+ if current_test_file != self.current_test_file:
47
+ self.current_test_file = current_test_file
48
+ print(f"\n{self.current_test_file}")
40
49
  pass # suppress test start lines
41
50
 
42
51
  def pytest_runtest_logreport(self, report: TestReport) -> None:
@@ -46,12 +55,20 @@ class MinimalReporter(TerminalReporter):
46
55
 
47
56
  test_name = report.nodeid.split("::")[-1]
48
57
  if report.passed:
49
- symbol = colored("✓", "green")
58
+ self.write("✓", green=True)
59
+ # symbol = colored("✓", "green")
50
60
  elif report.failed:
51
- symbol = colored("✗", "red")
61
+ self.write("✗", red=True)
62
+ # symbol = colored("✗", "red")
52
63
  elif report.skipped:
53
- symbol = colored("-", "yellow")
54
- print(f"{symbol} {test_name}")
64
+ self.write("-", yellow=True)
65
+ # symbol = colored("-", "yellow")
66
+ # self.write(f"{symbol} {test_name}")
67
+
68
+ self.write(f" {test_name}\n")
69
+
70
+ if self.print_errors and report.failed:
71
+ print(colored(report.longreprtext, "red"))
55
72
 
56
73
  def summary_stats(self) -> None:
57
74
  """Override result counts to suppress them."""
@@ -62,30 +79,53 @@ class MinimalReporter(TerminalReporter):
62
79
  pass # suppress final summary output
63
80
 
64
81
 
65
- def configure_pytest_reporter(config: Config) -> None:
82
+ # Environment Variable and CLI option to deactivate MinimalReporter
83
+ PYTEST_STANDARD_OUTPUT_ENV = "DEFAULT_TERMINAL_REPORTER"
84
+ PYTEST_STANDARD_OUTPUT_OPT = "--default-terminal-reporter"
85
+ if PYTEST_STANDARD_OUTPUT_OPT in sys.argv:
86
+ set_env_var(PYTEST_STANDARD_OUTPUT_ENV, "1", True)
87
+
88
+
89
+ def configure_pytest_reporter(config: Config, print_errors=True) -> None:
66
90
  """Configure the minimal reporter if terminalreporter plugin is disabled.
67
-
91
+
68
92
  Args:
69
93
  config: Pytest configuration object
70
94
  """
95
+ if env_vars.bool(PYTEST_STANDARD_OUTPUT_ENV, default=False):
96
+ return
97
+ minimal_reporter = MinimalReporter(config, print_errors=print_errors)
71
98
  # if terminalreporter plugin is disabled
72
99
  if "no:terminalreporter" in config.option.plugins:
73
100
  pluginmanager = config.pluginmanager
74
- pluginmanager.register(MinimalReporter(config), "minimal-reporter")
101
+ pluginmanager.register(minimal_reporter, "minimal-reporter")
102
+
103
+ if config.pluginmanager.has_plugin("terminalreporter"):
104
+ reporter = config.pluginmanager.get_plugin("terminalreporter")
105
+ config.pluginmanager.unregister(reporter, "terminalreporter")
106
+ config.pluginmanager.register(minimal_reporter, "terminalreporter")
75
107
 
76
108
 
77
- def run_pytest(test_path: str, breakpoints: bool, deactivate_pytest_output: bool = False, enable_print: bool = False) -> None:
109
+ def run_pytest(
110
+ test_path: str,
111
+ breakpoints: bool = False,
112
+ enable_print: bool = False,
113
+ pytest_args: list[str] | None = None
114
+ ) -> None:
78
115
  """Run pytest with customizable options for output control and debugging.
79
-
116
+
80
117
  Args:
81
118
  test_path: Path to the test file or directory to run
82
119
  breakpoints: If True, enables pytest debugger (--pdb) on failures
83
120
  deactivate_pytest_output: If True, uses minimal reporter instead of default output
84
121
  enable_print: If True, enables print statements in tests (-s flag)
85
122
  """
86
- args = []
87
- if deactivate_pytest_output:
88
- args += ["-p", "no:terminalreporter"]
123
+ if pytest_args:
124
+ args = pytest_args
125
+ if PYTEST_STANDARD_OUTPUT_OPT in args:
126
+ args.remove(PYTEST_STANDARD_OUTPUT_OPT)
127
+ else:
128
+ args = []
89
129
  if enable_print:
90
130
  args.append("-s") # -s disables output capturing
91
131
  if breakpoints:
emtest/testing_utils.py CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  import os
4
4
  import sys
5
+ import shutil
5
6
  import threading
6
7
  import time
7
8
  from time import sleep
@@ -11,15 +12,20 @@ from tqdm import TMonitor, tqdm
11
12
  from types import ModuleType
12
13
 
13
14
 
15
+ def are_we_in_docker() -> bool:
16
+ """Check if this code is running in a docker container."""
17
+ return os.path.exists('/.dockerenv')
18
+
19
+
14
20
  def add_path_to_python(src_path: str) -> None:
15
21
  """Add a directory to the Python path for importing modules.
16
-
22
+
17
23
  Removes the path if it already exists, then inserts it at the beginning
18
24
  of sys.path to ensure it takes priority over installed packages.
19
-
25
+
20
26
  Args:
21
27
  src_path: Directory path to add to Python path
22
-
28
+
23
29
  Raises:
24
30
  FileNotFoundError: If the path doesn't exist
25
31
  NotADirectoryError: If the path is not a directory
@@ -64,7 +70,7 @@ def assert_is_loaded_from_source(
64
70
 
65
71
  def polite_wait(n_sec: int) -> None:
66
72
  """Wait for the given duration, displaying a progress bar.
67
-
73
+
68
74
  Args:
69
75
  n_sec: Number of seconds to wait
70
76
  """
@@ -75,10 +81,10 @@ def polite_wait(n_sec: int) -> None:
75
81
 
76
82
  def await_thread_cleanup(timeout: int = 5) -> bool:
77
83
  """Wait for all threads to exit, with a timeout and progress bar.
78
-
84
+
79
85
  Args:
80
86
  timeout: Maximum seconds to wait for thread cleanup
81
-
87
+
82
88
  Returns:
83
89
  True if only the main thread remains, False if other threads persist
84
90
  """
@@ -95,4 +101,54 @@ def await_thread_cleanup(timeout: int = 5) -> bool:
95
101
 
96
102
  return len(get_threads()) == 1
97
103
 
104
+ def get_thread_names():
105
+ """Get a list of the names of all active threads."""
106
+ return [thread.name for thread in threading.enumerate()]
107
+
108
+ def set_env_var(name: str, value: str | bool | int, override: bool = True):
109
+ """
110
+ Set an environment variable with optional overriding behavior.
98
111
 
112
+ Args:
113
+ name (str): The name of the environment variable to set.
114
+ value (str): The value to assign to the environment variable.
115
+ override (bool, optional): Whether to overwrite the variable if it is already set.
116
+ Defaults to False.
117
+
118
+ Behavior:
119
+ - If 'override' is True, the environment variable will always be set to 'value'.
120
+ - If 'override' is False, the variable will only be set if it is not already defined.
121
+ """
122
+ if isinstance(value, bool):
123
+ value = int(value)
124
+ if isinstance(value, int):
125
+ value = str(value)
126
+ if override or name not in os.environ:
127
+ os.environ[name] = value
128
+
129
+ def make_dir(dir_path: str, clear_existing: bool = False) -> str:
130
+ """Create a directory, handling the case where it already exists."""
131
+ if clear_existing and os.path.exists(dir_path):
132
+ shutil.rmtree(dir_path)
133
+ if not os.path.isdir(dir_path):
134
+ os.makedirs(dir_path)
135
+ return dir_path
136
+
137
+
138
+ def delete_path(path: bool) -> bool:
139
+ """Delete the specified file or folder."""
140
+ if os.path.isdir(path):
141
+ shutil.rmtree(path)
142
+ elif os.path.exists(path):
143
+ os.remove(path)
144
+
145
+ def ensure_dirs_exist(*dirs):
146
+ """Ensure that all directories in the list exist."""
147
+ for dir in dirs:
148
+ os.makedirs(dir, exist_ok=True)
149
+
150
+
151
+ def ensure_dir_exists(dir_path):
152
+ if not os.path.exists(dir_path):
153
+ os.makedirs(dir_path)
154
+ return dir_path
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: emtest
3
- Version: 0.0.1
3
+ Version: 0.0.3
4
4
  Summary: Testing utilities which I find useful.
5
5
  Author-email: Emendir <dev@emendir.tech>
6
6
  License-Expression: CC0-1.0
@@ -0,0 +1,9 @@
1
+ _auto_run_with_pytest/__init__.py,sha256=XfdKb4Kz-BHojNZdUn_m1RJbgGM65-qUIUe-tLlc8S4,985
2
+ emtest/__init__.py,sha256=G_EnVC57IL_OjVJAjaz75y9owlCcdbvXfvR-ncO1icU,344
3
+ emtest/pytest_utils.py,sha256=qGKGlQTIgKi3BjmUTfcqzPE_0QfWkWfefy8Oitq3sdQ,4844
4
+ emtest/testing_utils.py,sha256=lzXRXwA50YQBHO35Y2s6NcbvgrdTcwGJjJ79CkzJKkk,4780
5
+ emtest-0.0.3.dist-info/licenses/LICENCE,sha256=bUia9ikmYtnjbTTOSUI3hJhKX25B17WPSbASZN9Z-gM,7047
6
+ emtest-0.0.3.dist-info/METADATA,sha256=4GAMKLottAS-zatp4qyF1e40U4vY_HtUIoZjAVYwdmE,1570
7
+ emtest-0.0.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
8
+ emtest-0.0.3.dist-info/top_level.txt,sha256=sfToBhqMjk35PDheZ42yiMGYNYatT3V5Mwhb79fp5dE,29
9
+ emtest-0.0.3.dist-info/RECORD,,
@@ -0,0 +1,2 @@
1
+ _auto_run_with_pytest
2
+ emtest
@@ -1,8 +0,0 @@
1
- emtest/__init__.py,sha256=tC-Hx6hKUVswMSIik-RwLF4fO4rDaZXi05DzDkixoCE,195
2
- emtest/pytest_utils.py,sha256=0vVNw6P4FdS7BsX18GMXK7bne-hfSNWiLtRS0sLiZOA,3486
3
- emtest/testing_utils.py,sha256=uZeEw4EKhy_2sUIzUPF1kvfVfyis3mF7Q5SSj9uxzCU,2899
4
- emtest-0.0.1.dist-info/licenses/LICENCE,sha256=bUia9ikmYtnjbTTOSUI3hJhKX25B17WPSbASZN9Z-gM,7047
5
- emtest-0.0.1.dist-info/METADATA,sha256=UWQ9-OOa4u74B_kwP8BG2-2c77u1Mjod5mXS71TnGOM,1570
6
- emtest-0.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
7
- emtest-0.0.1.dist-info/top_level.txt,sha256=8yXylA__VmNeIDW3MBOmSiOpUCZoEprrys-qdB54CLo,7
8
- emtest-0.0.1.dist-info/RECORD,,
@@ -1 +0,0 @@
1
- emtest
File without changes