7from testsuite
import types
8from testsuite.utils import cached_property, json_util, traceback, yaml_util
12 """Base class for errors from this module."""
16 """Unsupported file open mode passed."""
20 """Json file load or parse failure error."""
24 """Yaml file load or parse failure error."""
27__tracebackhide__ = traceback.hide(BaseError, FileNotFoundError)
31 """Generates sequence of paths for static files."""
35 filename: types.PathOrStr,
36 ) -> typing.Iterator[pathlib.Path]: ...
42 filename: types.PathOrStr,
43 directory: bool =
False,
44 ) -> typing.Iterator[pathlib.Path]: ...
48 """Returns path to static regular file."""
52 filename: types.PathOrStr,
55 ) -> pathlib.Path |
None: ...
59 """Returns path to static directory."""
63 filename: types.PathOrStr,
66 ) -> pathlib.Path |
None: ...
70 """Open static file by name.
72 Only read-only open modes are supported.
76 .. code-block:: python
78 def test_foo(open_file):
79 with open_file('foo') as fp:
85 filename: types.PathOrStr,
94 """Load file from static directory.
98 .. code-block:: python
100 def test_something(load):
101 data = load('filename')
103 :return: :py:class:`LoadFixture` callable instance.
108 filename: types.PathOrStr,
113 ) -> bytes | str |
None: ...
117 """Load binary data from static directory.
121 .. code-block:: python
123 def test_something(load_binary):
124 bytes_data = load_binary('data.bin')
127 def __call__(self, filename: types.PathOrStr) -> bytes: ...
131 """Load json doc from string.
133 Json loader runs ``json_util.loads(data, ..., *args, **kwargs)`` hooks.
135 * bson.json_util.object_hook()
136 * mockserver substitution
140 .. code-block:: python
142 def test_something(json_loads):
143 json_obj = json_loads('{"key": "value"}')
146 def __call__(self, content, *args, **kwargs) -> typing.Any: ...
150 """Load json doc from static directory.
152 Json loader runs ``json_util.loads(data, ..., *args, **kwargs)`` hooks.
154 * bson.json_util.object_hook()
155 * mockserver substitution
159 .. code-block:: python
161 def test_something(load_json):
162 json_obj = load_json('filename.json')
167 filename: types.PathOrStr,
176 """Load yaml doc from static directory.
178 .. code-block:: python
180 def test_something(load_yaml):
181 yaml_obj = load_yaml('filename.yaml')
186 filename: types.PathOrStr,
192_MODES_WHITELIST = frozenset([
'r',
'rt',
'rb'])
196def get_search_pathes(
197 _search_directories_existing: tuple[pathlib.Path, ...],
198 _path_entries_cache: typing.Callable,
199) -> GetSearchPathsFixture:
200 def search(filename: types.PathOrStr) -> typing.Iterator[pathlib.Path]:
201 for directory
in _search_directories_existing:
202 entry = _path_entries_cache(directory, filename)
210def get_search_paths(get_search_pathes):
211 return get_search_pathes
215def search_path(get_search_paths: GetSearchPathsFixture) -> SearchPathFixture:
217 filename: types.PathOrStr,
218 directory: bool =
False,
219 ) -> typing.Iterator[pathlib.Path]:
220 for abs_filename
in get_search_paths(filename):
222 if abs_filename.is_dir():
225 if abs_filename.is_file():
233 search_path: SearchPathFixture, _testsuite_file_not_found_error
234) -> GetFilePathFixture:
236 filename: types.PathOrStr,
239 ) -> pathlib.Path |
None:
240 for path
in search_path(filename):
244 raise _testsuite_file_not_found_error(
245 f
'File {filename} was not found',
253def get_directory_path(
254 search_path: SearchPathFixture,
255 _testsuite_file_not_found_error,
256) -> GetDirectoryPathFixture:
257 def get_directory_path(
258 filename: types.PathOrStr,
261 ) -> pathlib.Path |
None:
262 for path
in search_path(filename, directory=
True):
266 raise _testsuite_file_not_found_error(
267 f
'Directory {filename} was not found',
271 return get_directory_path
275def _testsuite_file_not_found_error(_search_directories_existing):
276 def raise_error(message, filename):
278 f
' - {path / filename}' for path
in _search_directories_existing
280 return FileNotFoundError(
281 f
'{message}\n\nThe following paths were examined:\n{paths}',
288def open_file(get_file_path: GetFilePathFixture) -> OpenFileFixture:
290 filename: types.PathOrStr,
296 if mode
not in _MODES_WHITELIST:
298 f
'Incorrect file open mode {mode!r} passed. '
299 f
'Only read-only modes are supported.',
302 get_file_path(filename),
313def load(get_file_path: GetFilePathFixture) -> LoadFixture:
314 """Returns a function that loads a static file as text.
316 Searches the file via @c get_file_path. Returns @c None when
317 @c missing_ok=True and the file is absent.
319 @ingroup userver_testsuite_fixtures
320 Part of the [yandex-taxi-testsuite](https://github.com/yandex/yandex-taxi-testsuite/blob/develop/testsuite/plugins/common.py#L313)
324 filename: types.PathOrStr,
329 ) -> bytes | str |
None:
330 path = get_file_path(filename, missing_ok=missing_ok)
333 return path.read_text(encoding=encoding, errors=errors)
339def load_binary(get_file_path: GetFilePathFixture) -> LoadBinaryFixture:
340 """Returns a function that loads a static file as bytes.
342 Searches the file via @c get_file_path.
344 @ingroup userver_testsuite_fixtures
345 Part of the [yandex-taxi-testsuite](https://github.com/yandex/yandex-taxi-testsuite/blob/develop/testsuite/plugins/common.py#L336)
348 def load_binary(filename: types.PathOrStr) -> bytes:
349 path = get_file_path(filename)
350 return path.read_bytes()
356def json_loads(object_hook, load_json_defaults) -> JsonLoadsFixture:
357 def json_loads(content, *args, **kwargs) -> typing.Any:
358 if 'object_hook' not in kwargs:
359 kwargs[
'object_hook'] = object_hook
361 return json_util.loads(
364 **load_json_defaults,
373 load: LoadFixture, json_loads: JsonLoadsFixture
375 """Returns a function that loads a static file as JSON.
377 @ingroup userver_testsuite_fixtures
378 Part of the [yandex-taxi-testsuite](https://github.com/yandex/yandex-taxi-testsuite/blob/develop/testsuite/plugins/common.py#L366)
382 filename: types.PathOrStr,
388 content =
load(filename, missing_ok=missing_ok)
392 return json_loads(content, *args, **kwargs)
393 except json.JSONDecodeError
as err:
395 f
'Failed to load JSON file {filename}',
402def load_yaml(load: LoadFixture) -> LoadYamlFixture:
403 """Returns a function that loads a static file as YAML.
405 @ingroup userver_testsuite_fixtures
406 Part of the [yandex-taxi-testsuite](https://github.com/yandex/yandex-taxi-testsuite/blob/develop/testsuite/plugins/common.py#L392)
410 filename: types.PathOrStr,
414 content =
load(filename)
416 return yaml_util.load(content, *args, **kwargs)
419 f
'Failed to load YAML file {filename}',
425FilePathsCache = dict[pathlib.Path, list[pathlib.Path]]
428def pytest_configure(config):
429 config.addinivalue_line(
431 'nofilldb: test does not need db initialization',
436def static_dir(testsuite_request_directory) -> pathlib.Path:
437 """Static directory related to test path.
439 Returns static directory relative to test file, e.g.
442 |- static/ <-- base static directory for test_foo.py
446 @ingroup userver_testsuite_fixtures
447 Part of the [yandex-taxi-testsuite](https://github.com/yandex/yandex-taxi-testsuite/blob/develop/testsuite/plugins/common.py#L422)
449 return testsuite_request_directory /
'static'
454 """Use this fixture to override base static search path.
458 def initial_data_path():
460 pathlib.Path(PROJECT_ROOT) / 'tests/static',
461 pathlib.Path(PROJECT_ROOT) / 'static',
465 @ingroup userver_testsuite_fixtures
466 Part of the [yandex-taxi-testsuite](https://github.com/yandex/yandex-taxi-testsuite/blob/develop/testsuite/plugins/common.py#L436)
473def get_all_static_file_paths(
474 static_dir: pathlib.Path,
475 _file_paths_cache: FilePathsCache,
477 def _get_file_paths() -> list[pathlib.Path]:
478 if static_dir
not in _file_paths_cache:
479 _file_paths_cache[static_dir] = [
480 path
for path
in static_dir.rglob(
'')
if path.is_file()
482 return _file_paths_cache[static_dir]
484 return _get_file_paths
489 """Perform object substitution as in load_json.
491 @ingroup userver_testsuite_fixtures
492 Part of the [yandex-taxi-testsuite](https://github.com/yandex/yandex-taxi-testsuite/blob/develop/testsuite/plugins/common.py#L468)
495 def _substitute(content, *args, **kwargs):
496 return json_util.substitute(
498 object_hook=object_hook,
506@pytest.fixture(scope='session')
507def testsuite_get_source_path():
508 def get_source_path(path) -> pathlib.Path:
509 return pathlib.Path(path)
511 return get_source_path
514@pytest.fixture(scope='session')
515def testsuite_get_source_directory(testsuite_get_source_path):
516 def get_source_directory(path) -> pathlib.Path:
517 return testsuite_get_source_path(path).parent
519 return get_source_directory
523def testsuite_request_path(request, testsuite_get_source_path) -> pathlib.Path:
524 return testsuite_get_source_path(request.module.__file__)
528def testsuite_request_directory(testsuite_request_path) -> pathlib.Path:
529 return testsuite_request_path.parent
532@pytest.fixture(scope='session')
533def worker_id(request) -> str:
534 if hasattr(request.config,
'workerinput'):
535 return request.config.workerinput[
'workerid']
539@pytest.fixture(scope='session')
540def _file_paths_cache() -> FilePathsCache:
545def _search_directories(
547 static_dir: pathlib.Path,
548 initial_data_path: tuple[pathlib.Path, ...],
549 testsuite_request_path,
551) -> tuple[pathlib.Path, ...]:
552 test_module_name = testsuite_request_path.stem
553 node_name = request.node.name
555 node_name = node_name[: node_name.index(
'[')]
556 search_directories = [
557 _path_entries_cache(static_dir, test_module_name, node_name),
558 _path_entries_cache(static_dir, test_module_name),
559 _path_entries_cache(static_dir,
'default'),
560 _path_entries_cache(static_dir,
''),
562 search_directories.extend(
563 _path_entries_cache(path)
for path
in initial_data_path
565 return tuple(search_directories)
569def _search_directories_existing(_search_directories):
570 return tuple(path
for path
in _search_directories
if path.is_dir())
573@pytest.fixture(scope='session')
574def load_json_defaults():
578@pytest.fixture(scope='session')
579def _cached_stat_path():
580 path_type = type(pathlib.Path())
581 stat_cache: dict[str, tuple[bool, FileNotFoundError | typing.Any]] = {}
582 glob_cache: dict[typing.Any, tuple] = {}
583 content_cache: dict[typing.Any, tuple | str | bytes] = {}
584 iterdir_cache: dict[str, tuple] = {}
586 class CachedStatPath(path_type):
587 def stat(self, *, follow_symlinks=True):
589 if key
in stat_cache:
590 is_exc, value = stat_cache[key]
595 value = super().stat()
596 stat_cache[key] = (
False, value)
598 except FileNotFoundError
as exc:
599 stat_cache[key] = (
True, exc)
603 cache_key = str(self)
604 data = iterdir_cache.get(cache_key)
606 data = tuple(super().iterdir())
607 content_cache[cache_key] = data
610 def read_bytes(self):
611 cache_key = (str(self),
'b')
612 data = content_cache.get(cache_key)
614 data = super().read_bytes()
615 content_cache[cache_key] = data
618 def read_text(self, encoding=None, errors=None):
619 cache_key = (str(self), encoding, errors)
620 data = content_cache.get(cache_key)
622 data = super().read_text(encoding=encoding, errors=errors)
623 content_cache[cache_key] = data
626 def glob(self, pattern):
627 key = (str(self), pattern)
628 if key
not in glob_cache:
629 data = glob_cache[key] = tuple(super().glob(pattern))
631 return glob_cache[key]
633 def rglob(self, pattern):
634 key = (
'r', str(self), pattern)
635 if key
not in glob_cache:
636 data = glob_cache[key] = tuple(super().rglob(pattern))
638 return glob_cache[key]
651 return super().is_dir()
655 return super().is_file()
659 return super().exists()
661 return CachedStatPath
664@pytest.fixture(scope='session')
665def _path_entries_cache(_cached_stat_path):
669 result = entries_cache.get(parts)
672 result = _cached_stat_path(*parts)
673 entries_cache[parts] = result