testsweet 0.2.1__tar.gz → 0.2.2__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.
- {testsweet-0.2.1 → testsweet-0.2.2}/CHANGELOG.md +13 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/PKG-INFO +1 -1
- {testsweet-0.2.1 → testsweet-0.2.2}/pyproject.toml +1 -1
- {testsweet-0.2.1 → testsweet-0.2.2}/src/testsweet/__main__.py +7 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/src/testsweet/_loaders.py +0 -4
- {testsweet-0.2.1 → testsweet-0.2.2}/src/testsweet/_targets.py +2 -2
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/cli.py +13 -0
- testsweet-0.2.2/tests/fixtures/runner/uses_relative_import.py +8 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/targets.py +15 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/uv.lock +1 -1
- {testsweet-0.2.1 → testsweet-0.2.2}/.github/workflows/lint.yml +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/.github/workflows/publish.yml +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/.github/workflows/tests.yml +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/.github/workflows/types.yml +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/.gitignore +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/.pre-commit-config.yaml +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/.python-version +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/CLAUDE.md +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/LICENSE +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/README.md +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/docs/contributing.md +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/docs/examples/catches.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/docs/examples/classes.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/docs/examples/functions.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/docs/getting-started.md +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/docs/img/testsweet_200x200.png +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/docs/reference.md +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/src/testsweet/__init__.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/src/testsweet/_assertion.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/src/testsweet/_catches.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/src/testsweet/_class_helpers.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/src/testsweet/_classify.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/src/testsweet/_condition_decorator.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/src/testsweet/_config.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/src/testsweet/_discover.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/src/testsweet/_markers.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/src/testsweet/_outcomes.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/src/testsweet/_params.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/src/testsweet/_plugins.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/src/testsweet/_report.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/src/testsweet/_resolve.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/src/testsweet/_runner.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/src/testsweet/_skip.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/src/testsweet/_tag.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/src/testsweet/_tag_filter.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/src/testsweet/_walk.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/src/testsweet/_xfail.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/src/testsweet/py.typed +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/__init__.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/catches.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/config.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/discover.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/__init__.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/empty.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/imported_only.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/mixed.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/mixed_local_imported.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/multiple.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/non_callable_marker.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/__init__.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/all_pass.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/assertion_diagnostics.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/class_calls_recorded.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/class_decorated_simple.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/class_decorated_with_cm.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/class_enter_only.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/class_enter_raises.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/class_exit_raises.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/class_method_fails.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/class_mixed_with_function.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/class_simple.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/class_test_context_raises.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/class_test_context_with_params.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/class_with_inheritance.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/class_with_test_context.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/class_with_underscore_methods.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/empty.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/has_broken_import.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/has_failure.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/keyboard_interrupt.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/non_assertion_error.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/orphan_params.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/orphan_skip.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/orphan_tag.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/orphan_xfail.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/params_empty.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/params_generator.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/params_lazy_generator.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/params_lazy_list.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/params_lazy_on_class_method.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/params_no_decoration.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/params_on_class_method.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/params_simple.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/params_with_failure.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/skip_on_class_method.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/skip_on_params.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/tagged_class.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/runner/xfail_on_params.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/fixtures/single.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/markers.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/outcomes.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/params.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/plugins.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/report.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/resolve.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/runner.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/skip.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/tag.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/tag_filter.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/test_unittest_shim.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/walk.py +0 -0
- {testsweet-0.2.1 → testsweet-0.2.2}/tests/xfail.py +0 -0
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
Changelog
|
|
2
2
|
=========
|
|
3
3
|
|
|
4
|
+
[0.2.2] (2026-06-27)
|
|
5
|
+
--------------------
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
- Fixed loading a test file by path (e.g. `testsweet path/to/tests.py`)
|
|
10
|
+
when the module uses relative imports.
|
|
11
|
+
- Fixed resolving dotted targets (e.g. `testsweet tests.foo`) when run
|
|
12
|
+
via the installed `testsweet` console script.
|
|
13
|
+
|
|
14
|
+
[0.2.2]: https://github.com/kaapstorm/testsweet/releases/tag/v0.2.2
|
|
15
|
+
|
|
16
|
+
|
|
4
17
|
[0.2.1] (2026-06-27)
|
|
5
18
|
--------------------
|
|
6
19
|
|
|
@@ -84,6 +84,13 @@ def main(argv: list[str]) -> int:
|
|
|
84
84
|
if (include or exclude) else None
|
|
85
85
|
)
|
|
86
86
|
with scoped_sys_path():
|
|
87
|
+
# `python -m testsweet` prepends the cwd to sys.path, but the
|
|
88
|
+
# installed console script does not (sys.path[0] is its own bin
|
|
89
|
+
# directory). Put the cwd on the path so dotted targets resolve
|
|
90
|
+
# against the project the same way under both invocations.
|
|
91
|
+
cwd = str(pathlib.Path.cwd())
|
|
92
|
+
if cwd not in sys.path:
|
|
93
|
+
sys.path.insert(0, cwd)
|
|
87
94
|
config = load_config(pathlib.Path.cwd())
|
|
88
95
|
plugins = load_plugins()
|
|
89
96
|
wrap_unit = unit_wrapper(plugins)
|
|
@@ -39,10 +39,6 @@ def _exec_module_from_path(path: pathlib.Path) -> ModuleType:
|
|
|
39
39
|
return module
|
|
40
40
|
|
|
41
41
|
|
|
42
|
-
def _load_path(target: str) -> ModuleType:
|
|
43
|
-
return _exec_module_from_path(pathlib.Path(target).resolve())
|
|
44
|
-
|
|
45
|
-
|
|
46
42
|
def _load_path_for_walk(path: pathlib.Path) -> ModuleType:
|
|
47
43
|
info = _dotted_name_for_path(path)
|
|
48
44
|
if info is None:
|
|
@@ -9,7 +9,7 @@ from types import ModuleType
|
|
|
9
9
|
|
|
10
10
|
from testsweet._classify import _resolve_dotted
|
|
11
11
|
from testsweet._config import DiscoveryConfig
|
|
12
|
-
from testsweet._loaders import
|
|
12
|
+
from testsweet._loaders import _load_path_for_walk
|
|
13
13
|
from testsweet._walk import (
|
|
14
14
|
_build_exclude_set,
|
|
15
15
|
_resolve_include_paths,
|
|
@@ -73,7 +73,7 @@ def parse_target(
|
|
|
73
73
|
excluded=excluded,
|
|
74
74
|
)
|
|
75
75
|
]
|
|
76
|
-
return [(
|
|
76
|
+
return [(_load_path_for_walk(path), None)]
|
|
77
77
|
return [_resolve_dotted(target)]
|
|
78
78
|
|
|
79
79
|
|
|
@@ -394,6 +394,19 @@ class Cli:
|
|
|
394
394
|
assert result.returncode == 0
|
|
395
395
|
assert result.stdout.startswith('Usage: testsweet')
|
|
396
396
|
|
|
397
|
+
def installed_entry_point_resolves_dotted_target(self):
|
|
398
|
+
script = pathlib.Path(sys.executable).with_name('testsweet')
|
|
399
|
+
if not script.exists():
|
|
400
|
+
return # not an installed environment; skip silently
|
|
401
|
+
result = subprocess.run(
|
|
402
|
+
[str(script), 'tests.fixtures.runner.all_pass'],
|
|
403
|
+
capture_output=True,
|
|
404
|
+
text=True,
|
|
405
|
+
cwd=_REPO_ROOT,
|
|
406
|
+
)
|
|
407
|
+
assert result.returncode == 0, result.stderr
|
|
408
|
+
assert 'passes_one ... ok' in result.stdout
|
|
409
|
+
|
|
397
410
|
@params([(('-h',),), (('--help',),)])
|
|
398
411
|
def help_flag_prints_usage_and_exits_zero(self, args):
|
|
399
412
|
result = _run_cli(*args)
|
|
@@ -65,6 +65,21 @@ class ParseTarget:
|
|
|
65
65
|
assert module is expected
|
|
66
66
|
assert names == expected_names
|
|
67
67
|
|
|
68
|
+
@params([
|
|
69
|
+
('tests/fixtures/runner/uses_relative_import.py',),
|
|
70
|
+
('./tests/fixtures/runner/uses_relative_import.py',),
|
|
71
|
+
(str((_FIXTURES / 'uses_relative_import.py').resolve()),),
|
|
72
|
+
])
|
|
73
|
+
def file_path_with_relative_import(self, target):
|
|
74
|
+
# A file path to a module inside a package must be loaded with
|
|
75
|
+
# package context so its relative imports resolve, the same way
|
|
76
|
+
# the equivalent dotted name does.
|
|
77
|
+
result = parse_target(target)
|
|
78
|
+
assert len(result) == 1
|
|
79
|
+
module, names = result[0]
|
|
80
|
+
assert names is None
|
|
81
|
+
assert hasattr(module, 'uses_rel')
|
|
82
|
+
|
|
68
83
|
def dotted_too_many_segments(self):
|
|
69
84
|
with catch_exceptions() as excs:
|
|
70
85
|
parse_target(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|