From eb3ae3d272e922019c5d5bba69d5289d4b3a223f Mon Sep 17 00:00:00 2001 From: Andre Anjos <andre.dos.anjos@gmail.com> Date: Mon, 30 Jan 2023 11:14:32 +0100 Subject: [PATCH] [tests] Fix docformatting --- src/ptbench/configs/datasets/__init__.py | 2 -- src/ptbench/data/dataset.py | 5 ----- src/ptbench/data/loader.py | 4 ---- src/ptbench/data/transforms.py | 3 ++- src/ptbench/data/utils.py | 2 -- src/ptbench/engine/evaluator.py | 3 --- src/ptbench/engine/predictor.py | 1 - src/ptbench/engine/trainer.py | 3 --- src/ptbench/models/alexnet.py | 2 -- src/ptbench/models/densenet.py | 1 - src/ptbench/models/densenet_rs.py | 2 -- src/ptbench/models/logistic_regression.py | 1 - src/ptbench/models/pasa.py | 2 -- src/ptbench/models/signs_to_tb.py | 1 - src/ptbench/scripts/aggregpred.py | 1 - src/ptbench/scripts/compare.py | 1 - src/ptbench/scripts/config.py | 15 ++++++++------- src/ptbench/scripts/dataset.py | 4 ---- src/ptbench/scripts/evaluate.py | 1 - src/ptbench/scripts/predtojson.py | 2 -- src/ptbench/scripts/train.py | 3 --- src/ptbench/scripts/train_analysis.py | 2 -- src/ptbench/utils/checkpointer.py | 1 - src/ptbench/utils/download.py | 1 - src/ptbench/utils/measure.py | 4 ---- src/ptbench/utils/plot.py | 4 ---- src/ptbench/utils/rc.py | 1 - src/ptbench/utils/resources.py | 8 -------- src/ptbench/utils/table.py | 1 - tests/conftest.py | 2 -- 30 files changed, 10 insertions(+), 73 deletions(-) diff --git a/src/ptbench/configs/datasets/__init__.py b/src/ptbench/configs/datasets/__init__.py index 1b074837..d5b0352a 100644 --- a/src/ptbench/configs/datasets/__init__.py +++ b/src/ptbench/configs/datasets/__init__.py @@ -49,7 +49,6 @@ def make_subset(samples, transforms=[], prefixes=[], suffixes=[]): subset : :py:class:`ptbench.data.utils.SampleListDataset` A pre-formatted dataset that can be fed to one of our engines """ - from ...data.utils import SampleListDataset as wrapper return wrapper(samples, prefixes + transforms + suffixes) @@ -184,7 +183,6 @@ def get_samples_weights(dataset): samples_weights : :py:class:`torch.Tensor` the weights for all the samples in the dataset given as input """ - samples_weights = [] if isinstance(dataset, torch.utils.data.ConcatDataset): diff --git a/src/ptbench/data/dataset.py b/src/ptbench/data/dataset.py index a87aaf0b..995f227c 100644 --- a/src/ptbench/data/dataset.py +++ b/src/ptbench/data/dataset.py @@ -102,7 +102,6 @@ class JSONDataset: errors : int Number of errors found """ - logger.info("Checking dataset...") errors = 0 for proto in self._protocols: @@ -146,7 +145,6 @@ class JSONDataset: A dictionary mapping subset names to lists of objects (respecting the ``key``, ``data`` interface). """ - fileobj = self._protocols[protocol] if isinstance(fileobj, (str, bytes, pathlib.Path)): if str(fileobj).endswith(".bz2"): @@ -246,7 +244,6 @@ class CSVDataset: errors : int Number of errors found """ - logger.info("Checking dataset...") errors = 0 for name in self._subsets.keys(): @@ -277,7 +274,6 @@ class CSVDataset: A dictionary mapping subset names to lists of objects (respecting the ``key``, ``data`` interface). """ - return {k: self.samples(k) for k in self._subsets.keys()} def samples(self, subset): @@ -301,7 +297,6 @@ class CSVDataset: subset : list A lists of objects (respecting the ``key``, ``data`` interface). """ - fileobj = self._subsets[subset] if isinstance(fileobj, (str, bytes, pathlib.Path)): with open(self._subsets[subset], newline="") as f: diff --git a/src/ptbench/data/loader.py b/src/ptbench/data/loader.py index 1e0f8923..2e03aa35 100644 --- a/src/ptbench/data/loader.py +++ b/src/ptbench/data/loader.py @@ -29,7 +29,6 @@ def load_pil(path): image : PIL.Image.Image A PIL image """ - return PIL.Image.open(path) @@ -49,7 +48,6 @@ def load_pil_baw(path): image : PIL.Image.Image A PIL image in grayscale mode """ - return load_pil(path).convert("L") @@ -69,7 +67,6 @@ def load_pil_rgb(path): image : PIL.Image.Image A PIL image in RGB mode """ - return load_pil(path).convert("RGB") @@ -99,7 +96,6 @@ def make_delayed(sample, loader, key=None): In which ``key`` is as provided and ``data`` can be accessed to trigger sample loading. """ - return DelayedSample( functools.partial(loader, sample), key=key or sample["data"], diff --git a/src/ptbench/data/transforms.py b/src/ptbench/data/transforms.py index 91d56111..19b1cb6b 100644 --- a/src/ptbench/data/transforms.py +++ b/src/ptbench/data/transforms.py @@ -26,7 +26,8 @@ class SingleAutoLevel16to8: This transform assumes that the input image is gray-scaled. - To auto-level, we calculate the maximum and the minimum of the image, and + To auto-level, we calculate the maximum and the minimum of the + image, and consider such a range should be mapped to the [0,255] range of the destination image. """ diff --git a/src/ptbench/data/utils.py b/src/ptbench/data/utils.py index b7437539..bc5bdbd4 100644 --- a/src/ptbench/data/utils.py +++ b/src/ptbench/data/utils.py @@ -59,7 +59,6 @@ class SampleListDataset(torch.utils.data.Dataset): An optional list of transforms to set in the copy. If not specified, use ``self.transforms``. """ - return SampleListDataset(self._samples, transforms or self.transforms) def random_permute(self, feature): @@ -113,7 +112,6 @@ class SampleListDataset(torch.utils.data.Dataset): The sample data: ``[key, image, label]`` """ - if isinstance(key, slice): return [self[k] for k in range(*key.indices(len(self)))] else: # we try it as an int diff --git a/src/ptbench/engine/evaluator.py b/src/ptbench/engine/evaluator.py index 3c0348c0..5e86ee85 100644 --- a/src/ptbench/engine/evaluator.py +++ b/src/ptbench/engine/evaluator.py @@ -37,7 +37,6 @@ def eer_threshold(neg, pos) -> float: Threshold """ - from scipy.interpolate import interp1d from scipy.optimize import brentq @@ -52,7 +51,6 @@ def eer_threshold(neg, pos) -> float: def posneg(pred, gt, threshold): """Calculates true and false positives and negatives.""" - # threshold binary_pred = torch.gt(pred, threshold) @@ -107,7 +105,6 @@ def sample_measures_for_threshold(pred, gt, threshold): f1_score: float """ - tp_tensor, fp_tensor, tn_tensor, fn_tensor = posneg(pred, gt, threshold) # calc measures from scalars diff --git a/src/ptbench/engine/predictor.py b/src/ptbench/engine/predictor.py index 7becc675..27d9d214 100644 --- a/src/ptbench/engine/predictor.py +++ b/src/ptbench/engine/predictor.py @@ -72,7 +72,6 @@ def run(model, data_loader, name, device, output_folder, grad_cams=False): all_predictions : list All the predictions associated with filename and groundtruth """ - output_folder = os.path.join(output_folder, name) logger.info(f"Output folder: {output_folder}") diff --git a/src/ptbench/engine/trainer.py b/src/ptbench/engine/trainer.py index 2c6be7e1..2e3d91a6 100644 --- a/src/ptbench/engine/trainer.py +++ b/src/ptbench/engine/trainer.py @@ -44,7 +44,6 @@ def torch_evaluation(model): model : :py:class:`torch.nn.Module` Network """ - model.eval() yield model model.train() @@ -217,7 +216,6 @@ def train_epoch(loader, model, optimizer, device, criterion, batch_chunk_count): A floating-point value corresponding the weighted average of this epoch's loss """ - losses_in_epoch = [] samples_in_epoch = [] losses_in_batch = [] @@ -308,7 +306,6 @@ def validate_epoch(loader, model, device, criterion, pbar_desc): A floating-point value corresponding the weighted average of this epoch's loss """ - batch_losses = [] samples_in_batch = [] diff --git a/src/ptbench/models/alexnet.py b/src/ptbench/models/alexnet.py index 7248d306..ea096ecb 100644 --- a/src/ptbench/models/alexnet.py +++ b/src/ptbench/models/alexnet.py @@ -44,7 +44,6 @@ class Alexnet(nn.Module): tensor : :py:class:`torch.Tensor` """ - return self.model_ft(x) @@ -56,7 +55,6 @@ def build_alexnet(pretrained=False): module : :py:class:`torch.nn.Module` """ - model = Alexnet(pretrained=pretrained) model = [("normalizer", TorchVisionNormalizer()), ("model", model)] model = nn.Sequential(OrderedDict(model)) diff --git a/src/ptbench/models/densenet.py b/src/ptbench/models/densenet.py index 2b28be03..7a98acac 100644 --- a/src/ptbench/models/densenet.py +++ b/src/ptbench/models/densenet.py @@ -54,7 +54,6 @@ def build_densenet(pretrained=False, nb_channels=3): module : :py:class:`torch.nn.Module` """ - model = Densenet(pretrained=pretrained) model = [ ("normalizer", TorchVisionNormalizer(nb_channels=nb_channels)), diff --git a/src/ptbench/models/densenet_rs.py b/src/ptbench/models/densenet_rs.py index a5836930..c4448fbc 100644 --- a/src/ptbench/models/densenet_rs.py +++ b/src/ptbench/models/densenet_rs.py @@ -40,7 +40,6 @@ class DensenetRS(nn.Module): tensor : :py:class:`torch.Tensor` """ - return self.model_ft(x) @@ -52,7 +51,6 @@ def build_densenetrs(): module : :py:class:`torch.nn.Module` """ - model = DensenetRS() model = [("normalizer", TorchVisionNormalizer()), ("model", model)] model = nn.Sequential(OrderedDict(model)) diff --git a/src/ptbench/models/logistic_regression.py b/src/ptbench/models/logistic_regression.py index 7b001061..7e7818c7 100644 --- a/src/ptbench/models/logistic_regression.py +++ b/src/ptbench/models/logistic_regression.py @@ -41,7 +41,6 @@ def build_logistic_regression(input_size): module : :py:class:`torch.nn.Module` """ - model = LogisticRegression(input_size) model.name = "logistic_regression" return model diff --git a/src/ptbench/models/pasa.py b/src/ptbench/models/pasa.py index f0f66727..10e6cede 100644 --- a/src/ptbench/models/pasa.py +++ b/src/ptbench/models/pasa.py @@ -82,7 +82,6 @@ class PASA(nn.Module): tensor : :py:class:`torch.Tensor` """ - # First convolution block _x = x x = F.relu(self.batchNorm2d_4(self.fc1(x))) # 1st convolution @@ -137,7 +136,6 @@ def build_pasa(): module : :py:class:`torch.nn.Module` """ - model = PASA() model = [ ("normalizer", TorchVisionNormalizer(nb_channels=1)), diff --git a/src/ptbench/models/signs_to_tb.py b/src/ptbench/models/signs_to_tb.py index 1958282d..f3b3d5ea 100644 --- a/src/ptbench/models/signs_to_tb.py +++ b/src/ptbench/models/signs_to_tb.py @@ -48,7 +48,6 @@ def build_signs_to_tb(input_size, hidden_size): module : :py:class:`torch.nn.Module` """ - model = SignsToTB(input_size, hidden_size) model.name = "signs_to_tb" return model diff --git a/src/ptbench/scripts/aggregpred.py b/src/ptbench/scripts/aggregpred.py index 2d9aeac4..cf43f3d0 100644 --- a/src/ptbench/scripts/aggregpred.py +++ b/src/ptbench/scripts/aggregpred.py @@ -36,7 +36,6 @@ logger = setup(__name__.split(".")[0], format="%(levelname)s: %(message)s") @verbosity_option(logger=logger, expose_value=False) def aggregpred(label_path, output_folder) -> None: """Aggregate multiple predictions csv files into one.""" - import os import re import shutil diff --git a/src/ptbench/scripts/compare.py b/src/ptbench/scripts/compare.py index 5b5e006d..779c21ca 100644 --- a/src/ptbench/scripts/compare.py +++ b/src/ptbench/scripts/compare.py @@ -15,7 +15,6 @@ def _validate_threshold(t, dataset): Returns parsed threshold. """ - if t is None: return t diff --git a/src/ptbench/scripts/config.py b/src/ptbench/scripts/config.py index 00fe26ab..edb47b2d 100644 --- a/src/ptbench/scripts/config.py +++ b/src/ptbench/scripts/config.py @@ -21,10 +21,14 @@ def _retrieve_entry_points( ) -> typing.Iterable[importlib.metadata.EntryPoint]: """Wraps various entry-point retrieval mechanisms. - For Python 3.9 and 3.10, :py:func:`importlib.metadata.entry_points()` - returns a dictionary keyed by entry-point group names. From Python 3.10 - onwards, one may pass the ``group`` keyword to that function to enable - pre-filtering, or use the ``select()`` method on the returned value, which + For Python 3.9 and 3.10, + :py:func:`importlib.metadata.entry_points()` + returns a dictionary keyed by entry-point group names. From Python + 3.10 + onwards, one may pass the ``group`` keyword to that function to + enable + pre-filtering, or use the ``select()`` method on the returned value, + which is no longer a dictionary. For anything before Python 3.8, you must use the backported library @@ -70,7 +74,6 @@ def config(): @verbosity_option(logger=logger) def list(verbose) -> None: """Lists configuration files installed.""" - entry_points = _retrieve_entry_points("ptbench.config") entry_point_dict = {k.name: k for k in entry_points} @@ -156,7 +159,6 @@ def list(verbose) -> None: @verbosity_option(logger=logger) def describe(name, verbose) -> None: """Describes a specific configuration file.""" - entry_points = _retrieve_entry_points("ptbench.config") entry_point_dict = {k.name: k for k in entry_points} @@ -206,7 +208,6 @@ def describe(name, verbose) -> None: @verbosity_option(logger=logger, expose_value=False) def copy(source, destination) -> None: """Copy a specific configuration resource so it can be modified locally.""" - import shutil entry_points = _retrieve_entry_points("ptbench.config") diff --git a/src/ptbench/scripts/dataset.py b/src/ptbench/scripts/dataset.py index d5c277c5..0a27ce56 100644 --- a/src/ptbench/scripts/dataset.py +++ b/src/ptbench/scripts/dataset.py @@ -17,7 +17,6 @@ logger = setup(__name__.split(".")[0], format="%(levelname)s: %(message)s") def _get_supported_datasets(): """Returns a list of supported dataset names.""" - basedir = importlib.resources.files(__name__.split(".", 1)[0]).joinpath( "data" ) @@ -36,7 +35,6 @@ def _get_installed_datasets() -> dict[str, str]: * group(0): the name of the key for the dataset directory * group("name"): the short name for the dataset """ - from ..utils.rc import load_rc return dict(load_rc().get("datadir", {})) @@ -78,7 +76,6 @@ def dataset() -> None: @verbosity_option(logger=logger, expose_value=False) def list(): """Lists all supported and configured datasets.""" - supported = _get_supported_datasets() installed = _get_installed_datasets() @@ -129,7 +126,6 @@ def list(): @verbosity_option(logger=logger, expose_value=False) def check(dataset, limit): """Checks file access on one or more datasets.""" - import importlib to_check = _get_installed_datasets() diff --git a/src/ptbench/scripts/evaluate.py b/src/ptbench/scripts/evaluate.py index 85b20b11..5100f79d 100644 --- a/src/ptbench/scripts/evaluate.py +++ b/src/ptbench/scripts/evaluate.py @@ -15,7 +15,6 @@ def _validate_threshold(t, dataset): Returns parsed threshold. """ - if t is None: return 0.5 diff --git a/src/ptbench/scripts/predtojson.py b/src/ptbench/scripts/predtojson.py index ce9a23be..0e90343a 100644 --- a/src/ptbench/scripts/predtojson.py +++ b/src/ptbench/scripts/predtojson.py @@ -38,7 +38,6 @@ def _load(data): def _to_double_tensor(col): """Converts a column in a dataframe to a tensor array.""" - pattern = re.compile(" +") return col.apply(lambda cell: numpy.array(eval(pattern.sub(",", cell)))) @@ -82,7 +81,6 @@ def _load(data): @verbosity_option(logger=logger, expose_value=False) def predtojson(label_path, output_folder) -> None: """Convert predictions to dataset.""" - import os import shutil diff --git a/src/ptbench/scripts/train.py b/src/ptbench/scripts/train.py index d14c133d..754e603c 100644 --- a/src/ptbench/scripts/train.py +++ b/src/ptbench/scripts/train.py @@ -30,7 +30,6 @@ def setup_pytorch_device(name): device : :py:class:`torch.device` The pytorch device to use, pre-configured (and checked) """ - import torch if name.startswith("cuda:"): @@ -74,7 +73,6 @@ def set_seeds(value, all_gpus): If set, then reset the seed on all GPUs available at once. This is normally **not** what you want if running on a single GPU """ - import random import numpy.random @@ -100,7 +98,6 @@ def set_reproducible_cuda(): Reference: `PyTorch page for reproducibility <https://pytorch.org/docs/stable/notes/randomness.html>`_. """ - import torch.backends.cudnn # ensure to use only optimization algos for cuda that are known to have diff --git a/src/ptbench/scripts/train_analysis.py b/src/ptbench/scripts/train_analysis.py index f5d0bc32..f32c801a 100644 --- a/src/ptbench/scripts/train_analysis.py +++ b/src/ptbench/scripts/train_analysis.py @@ -30,7 +30,6 @@ def _loss_evolution(df): matplotlib.figure.Figure: Figure to be displayed or saved to file """ - import numpy figure = plt.figure() @@ -91,7 +90,6 @@ def _hardware_utilisation(df, const): matplotlib.figure.Figure: figure to be displayed or saved to file """ - figure = plt.figure() axes = figure.gca() diff --git a/src/ptbench/utils/checkpointer.py b/src/ptbench/utils/checkpointer.py index 8daf8a3e..36d10e5a 100644 --- a/src/ptbench/utils/checkpointer.py +++ b/src/ptbench/utils/checkpointer.py @@ -66,7 +66,6 @@ class Checkpointer: partial : :py:class:`bool`, Optional If True, loading is not strict and only the model is loaded """ - if f is None: f = self.last_checkpoint() diff --git a/src/ptbench/utils/download.py b/src/ptbench/utils/download.py index 9c780c7e..911d5f91 100644 --- a/src/ptbench/utils/download.py +++ b/src/ptbench/utils/download.py @@ -30,7 +30,6 @@ def download_to_tempfile(url, progress=False): f : :py:func:`tempfile.NamedTemporaryFile` A named temporary file that contains the downloaded URL """ - file_size = 0 response = urllib.request.urlopen(url) meta = response.info() diff --git a/src/ptbench/utils/measure.py b/src/ptbench/utils/measure.py index 7b03cc9c..f0031c0c 100644 --- a/src/ptbench/utils/measure.py +++ b/src/ptbench/utils/measure.py @@ -35,7 +35,6 @@ def tricky_division(n, d): Returns 0.0 in case of a division by zero """ - return n / (d + (d == 0)) @@ -114,7 +113,6 @@ def base_measures(tp, fp, tn, fn): one needs to consider the true abscence of annotations in a region as part of the measure. """ - return ( tricky_division(tp, tp + fp), # precision tricky_division(tp, tp + fn), # recall @@ -227,7 +225,6 @@ def beta_credible_region(successes, failures, lambda_, coverage): lower, upper: float The lower and upper bounds of the credible region """ - # we return the equally-tailed range right = (1.0 - coverage) / 2 # half-width in each side lower = scipy.special.betaincinv( @@ -359,7 +356,6 @@ def bayesian_measures(tp, fp, tn, fn, lambda_, coverage): better proxy if one needs to consider the true abscence of annotations in a region as part of the measure. """ - return ( beta_credible_region(tp, fp, lambda_, coverage), # precision beta_credible_region(tp, fn, lambda_, coverage), # recall diff --git a/src/ptbench/utils/plot.py b/src/ptbench/utils/plot.py index e71532f3..fd6bc1e8 100644 --- a/src/ptbench/utils/plot.py +++ b/src/ptbench/utils/plot.py @@ -47,7 +47,6 @@ def _precision_recall_canvas(title=None): axes : matplotlib.figure.Axes An axis set where to precision-recall plots should be added to """ - fig, axes1 = plt.subplots(1) # Names and bounds @@ -133,7 +132,6 @@ def precision_recall_f1iso(data): figure : matplotlib.figure.Figure A matplotlib figure you can save or display (uses an ``agg`` backend) """ - lines = ["-", "--", "-.", ":"] colors = [ "#1f77b4", @@ -211,7 +209,6 @@ def roc_curve(data, title=None): figure : matplotlib.figure.Figure A matplotlib figure you can save or display (uses an ``agg`` backend) """ - fig, axes = plt.subplots(1) # Names and bounds @@ -293,7 +290,6 @@ def relevance_analysis_plot(data, title=None): figure : matplotlib.figure.Figure A matplotlib figure you can save or display (uses an ``agg`` backend) """ - fig, axes = plt.subplots(1, 1, figsize=(6, 6)) # Names and bounds diff --git a/src/ptbench/utils/rc.py b/src/ptbench/utils/rc.py index 9f6bf41f..25049ce1 100644 --- a/src/ptbench/utils/rc.py +++ b/src/ptbench/utils/rc.py @@ -7,5 +7,4 @@ from exposed.rc import UserDefaults def load_rc() -> UserDefaults: """Returns global configuration variables.""" - return UserDefaults("ptbench.toml") diff --git a/src/ptbench/utils/resources.py b/src/ptbench/utils/resources.py index d146b53c..d9a1baf6 100644 --- a/src/ptbench/utils/resources.py +++ b/src/ptbench/utils/resources.py @@ -53,7 +53,6 @@ def run_nvidia_smi(query, rename=None): returns ``None``. Percentage information is left alone, memory information is transformed to gigabytes (floating-point). """ - if _nvidia_smi is not None: if rename is None: @@ -100,7 +99,6 @@ def gpu_constants(): * ``memory.total``, as ``gpu_memory_total`` (transformed to gigabytes, :py:class:`float`) """ - return run_nvidia_smi( ("gpu_name", "driver_version", "memory.total"), ("gpu_name", "gpu_driver_version", "gpu_memory_total"), @@ -130,7 +128,6 @@ def gpu_log(): * ``utilization.gpu``, as ``gpu_percent``, (:py:class:`float`, in percent) """ - retval = run_nvidia_smi( ( "memory.total", @@ -168,7 +165,6 @@ def cpu_constants(): in gigabytes 1. ``cpu_count`` (:py:class:`int`): number of logical CPUs available """ - return ( ("cpu_memory_total", psutil.virtual_memory().total / GB), ("cpu_count", psutil.cpu_count(logical=True)), @@ -215,7 +211,6 @@ class CPULogger: 5. ``cpu_open_files`` (:py:class:`int`): total number of open files by self and children """ - # check all cluster components and update process list # done so we can keep the cpu_percent() initialization stored_children = set(self.cluster[1:]) @@ -296,7 +291,6 @@ class _InformationGatherer: def summary(self): """Returns the current data.""" - if len(self.data[0]) == 0: self.logger.error("CPU/GPU logger was not able to collect any data") retval = [] @@ -330,7 +324,6 @@ def _monitor_worker(interval, has_gpu, main_pid, stop, queue, logging_level): logging_level: int The logging level to use for logging from launched processes """ - logger = multiprocessing.log_to_stderr(level=logging_level) ra = _InformationGatherer(has_gpu, main_pid, logger) @@ -397,7 +390,6 @@ class ResourceMonitor: def __enter__(self): """Starts the monitoring process.""" - self.monitor.start() return self diff --git a/src/ptbench/utils/table.py b/src/ptbench/utils/table.py index 78d321a6..cb4594b9 100644 --- a/src/ptbench/utils/table.py +++ b/src/ptbench/utils/table.py @@ -44,7 +44,6 @@ def performance_table(data, fmt): table : str A table in a specific format """ - headers = [ "Dataset", "T", diff --git a/tests/conftest.py b/tests/conftest.py index f268377b..f1aa8c92 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -10,13 +10,11 @@ import pytest @pytest.fixture def datadir(request) -> pathlib.Path: """Returns the directory in which the test is sitting.""" - return pathlib.Path(request.module.__file__).parents[0] / "data" def pytest_configure(config): """This function is run once for pytest setup.""" - config.addinivalue_line( "markers", "skip_if_rc_var_not_set(name): this mark skips the test if a certain " -- GitLab