--- a +++ b/pyproject.toml @@ -0,0 +1,462 @@ +[project] +name = "edsnlp" +description = "Modular, fast NLP framework, compatible with Pytorch and spaCy, offering tailored support for French clinical notes." +authors = [ + { name = "Data Science - DSN APHP", email = "perceval.wajsburt@aphp.fr" } +] +license = { file = "LICENSE" } +readme = "README.md" +requires-python = ">=3.7.1" +dynamic = ['version'] +dependencies = [ + "loguru", + "pytz", + "pysimstring>=1.2.1", + "regex", + # spacy doesn't provide binaries for python<3.9 from 3.8.2 so we need to cap it ourself + "spacy>=3.2,<3.8.2; python_version<'3.9'", + "spacy>=3.8.5,<4.0.0; python_version>='3.9'", + # thinc doesn't provide binaries for python<3.9 from 8.2.5 so we need to cap it ourself + "thinc<8.2.5; python_version<'3.9'", + "thinc>=8.2.5; python_version>='3.9'", + # blis>1.2.0 (dependency of thinc) doesn't provide binaries for python<3.10 so we need to cap it ourself + "blis<1.0.0; python_version<'3.9'", + "blis<1.2.1; python_version>='3.9' and python_version<'3.10'", + "confit>=0.7.3", + "tqdm", + "umls-downloader>=0.1.1", + "numpy>=1.15.0", + "pandas>=1.1.0; python_version<'3.8'", + "pandas>=1.4.0; python_version>='3.8'", + "typing-extensions>=4.0.0", + "dill", + # Packaging + "build>=1.0.0", + "toml", + "xxhash", + "pyarrow>=3.0.0", # support for fragment.metadata.num_rows + "fsspec; python_version>='3.8'", + "fsspec<2023.1.0; python_version<'3.8'", + # this is only to avoid backtracking issues with spacy's capping + "pydantic>=1.10.2", + "pydantic<2.0.0; python_version<'3.8'", + "pydantic-core<2.0.0; python_version<'3.8'", +] +[project.optional-dependencies] +dev-no-ml = [ + "pre-commit>=2.0.0; python_version<'3.8'", + "pre-commit>=2.21.0; python_version>='3.8'", + "pytest>=7.1.0", + "pytest-cov>=3.0.0", + "pytest-timeout", + + # Data libs + "koalas>=1.8.1; python_version<'3.8'", + "pyspark", + "polars", + + "scikit-learn", + + # Packaging + "poetry", + + "edsnlp[docs-no-ml]", +] +docs-no-ml = [ + "mike~=1.1.2", + "mkdocs-charts-plugin==0.0.8", + "mkdocs-img2fig-plugin==0.9.3", + "mkdocs-material~=9.2.0", + "mkdocs-section-index==0.3.4", + "mkdocs~=1.5.2", + "mkdocstrings>=0.20,<0.28.0", + "mkdocstrings-python~=1.1", + "mkdocs-minify-plugin", + "mkdocs-redirects>=1.2.1;python_version>='3.8'", + "pybtex~=0.24.0", + "pathspec>=0.11.1", # required by vendored mkdocs-autorefs PR + "astunparse", + "griffe<0.39", + "jedi", + "html5lib", +] +ml = [ + "rich-logger>=0.3.1", + # TODO: uv doesn't seem to resolve torch correctly, unless we cap it ourself + "torch>=1.13.0,<2.0.0; python_version<'3.8'", + "torch>=1.13.0,<2.5.0; python_version<'3.9'", + "torch>=1.13.0; python_version>='3.9'", + "foldedtensor>=0.4.0", + "safetensors>=0.3.0; python_version>='3.8'", + "safetensors>=0.3.0,<0.5.0; python_version<'3.8'", + "transformers>=4.0.0,<5.0.0", + "accelerate>=0.20.3,<1.0.0", +] +docs = [ + "edsnlp[docs-no-ml]", + "edsnlp[ml]", +] +dev = [ + "edsnlp[dev-no-ml]", + "edsnlp[ml]", + "optuna>=4.0.0", + "plotly>=5.18.0", # required by optuna viz + "ruamel.yaml>=0.18.0", + "configobj>=5.0.9", + "scikit-learn", +] +setup = [ + "mlconjug3<3.9.0", # bug https://github.com/Ars-Linguistica/mlconjug3/pull/506 + "numpy<2", # mlconjug has scikit-learn dep which doesn't support for numpy 2 yet +] + +[project.urls] +"Source Code" = "https://github.com/aphp/edsnlp" +"Documentation" = "https://aphp.github.io/edsnlp" +"Demo" = "https://aphp.github.io/edsnlp/demo" +"Bug Tracker" = "https://github.com/aphp/edsnlp/issues" + +[tool.setuptools.dynamic] +version = { attr = "edsnlp.__version__" } + +[tool.setuptools.package-data] +"edsnlp" = [ + "**/*.pyx", + "**/*.pxd", + "**/*.pxi", + "resources/*.csv", + "resources/*.json", + "resources/*.csv.gz", + "resources/*.json.gz", +] + +[tool.setuptools.packages.find] +where = ["."] + +[project.entry-points."edsnlp_core"] +"pipeline" = "edsnlp.core.pipeline:Pipeline" +"load" = "edsnlp.core.pipeline:load" +"optimizer" = "edsnlp.training.optimizer:ScheduledOptimizer" + +[project.entry-points."spacy_factories"] +# Core +"eds.accents" = "edsnlp.pipes.core.normalizer.accents.factory:create_component" +"eds.contextual_matcher" = "edsnlp.pipes.core.contextual_matcher.factory:create_component" +"eds.endlines" = "edsnlp.pipes.core.endlines.factory:create_component" +"eds.matcher" = "edsnlp.pipes.core.matcher.factory:create_component" +"eds.normalizer" = "edsnlp.pipes.core.normalizer.factory:create_component" +"eds.pollution" = "edsnlp.pipes.core.normalizer.pollution.factory:create_component" +"eds.quotes" = "edsnlp.pipes.core.normalizer.quotes.factory:create_component" +"eds.remove_lowercase" = "edsnlp.pipes.core.normalizer.remove_lowercase.factory:create_component" +"eds.sentences" = "edsnlp.pipes.core.sentences.factory:create_component" +"eds.spaces" = "edsnlp.pipes.core.normalizer.spaces.factory:create_component" +"eds.terminology" = "edsnlp.pipes.core.terminology.factory:create_component" + +# NER +"eds.adicap" = "edsnlp.pipes.ner.adicap.factory:create_component" +"eds.emergency_ccmu" = "edsnlp.pipes.ner.scores.emergency.ccmu.factory:create_component" +"eds.charlson" = "edsnlp.pipes.ner.scores.charlson.factory:create_component" +"eds.cim10" = "edsnlp.pipes.ner.cim10.factory:create_component" +"eds.covid" = "edsnlp.pipes.ner.covid.factory:create_component" +"eds.drugs" = "edsnlp.pipes.ner.drugs.factory:create_component" +"eds.elston_ellis" = "edsnlp.pipes.ner.scores.elston_ellis.factory:create_component" +"eds.emergency_gemsa" = "edsnlp.pipes.ner.scores.emergency.gemsa.factory:create_component" +"eds.emergency_priority" = "edsnlp.pipes.ner.scores.emergency.priority.factory:create_component" +"eds.score" = "edsnlp.pipes.ner.scores.factory:create_component" +"eds.sofa" = "edsnlp.pipes.ner.scores.sofa.factory:create_component" +"eds.tnm" = "edsnlp.pipes.ner.tnm.factory:create_component" +"eds.umls" = "edsnlp.pipes.ner.umls.factory:create_component" +"eds.suicide_attempt" = "edsnlp.pipes.ner.suicide_attempt.factory:create_component" + +# NER/Comorbidities +"eds.aids" = "edsnlp.pipes.ner.disorders.aids.factory:create_component" +"eds.alcohol" = "edsnlp.pipes.ner.behaviors.alcohol.factory:create_component" +"eds.cerebrovascular_accident" = "edsnlp.pipes.ner.disorders.cerebrovascular_accident.factory:create_component" +"eds.ckd" = "edsnlp.pipes.ner.disorders.ckd.factory:create_component" +"eds.congestive_heart_failure" = "edsnlp.pipes.ner.disorders.congestive_heart_failure.factory:create_component" +"eds.connective_tissue_disease" = "edsnlp.pipes.ner.disorders.connective_tissue_disease.factory:create_component" +"eds.copd" = "edsnlp.pipes.ner.disorders.copd.factory:create_component" +"eds.dementia" = "edsnlp.pipes.ner.disorders.dementia.factory:create_component" +"eds.diabetes" = "edsnlp.pipes.ner.disorders.diabetes.factory:create_component" +"eds.hemiplegia" = "edsnlp.pipes.ner.disorders.hemiplegia.factory:create_component" +"eds.leukemia" = "edsnlp.pipes.ner.disorders.leukemia.factory:create_component" +"eds.liver_disease" = "edsnlp.pipes.ner.disorders.liver_disease.factory:create_component" +"eds.lymphoma" = "edsnlp.pipes.ner.disorders.lymphoma.factory:create_component" +"eds.myocardial_infarction" = "edsnlp.pipes.ner.disorders.myocardial_infarction.factory:create_component" +"eds.peptic_ulcer_disease" = "edsnlp.pipes.ner.disorders.peptic_ulcer_disease.factory:create_component" +"eds.peripheral_vascular_disease" = "edsnlp.pipes.ner.disorders.peripheral_vascular_disease.factory:create_component" +"eds.solid_tumor" = "edsnlp.pipes.ner.disorders.solid_tumor.factory:create_component" +"eds.tobacco" = "edsnlp.pipes.ner.behaviors.tobacco.factory:create_component" + +# Span classifiers +"eds.family" = "edsnlp.pipes.qualifiers.family.factory:create_component" +"eds.history" = "edsnlp.pipes.qualifiers.history.factory:create_component" +"eds.hypothesis" = "edsnlp.pipes.qualifiers.hypothesis.factory:create_component" +"eds.negation" = "edsnlp.pipes.qualifiers.negation.factory:create_component" +"eds.reported_speech" = "edsnlp.pipes.qualifiers.reported_speech.factory:create_component" + +# Misc +"eds.consultation_dates" = "edsnlp.pipes.misc.consultation_dates.factory:create_component" +"eds.dates" = "edsnlp.pipes.misc.dates.factory:create_component" +"eds.quantities" = "edsnlp.pipes.misc.quantities.factory:create_component" +"eds.reason" = "edsnlp.pipes.misc.reason.factory:create_component" +"eds.sections" = "edsnlp.pipes.misc.sections.factory:create_component" +"eds.tables" = "edsnlp.pipes.misc.tables.factory:create_component" + +# Data +"eds.split" = "edsnlp.pipes.misc.split.split:Split" +"eds.standoff_dict2doc" = "edsnlp.data.converters:StandoffDict2DocConverter" +"eds.standoff_doc2dict" = "edsnlp.data.converters:StandoffDoc2DictConverter" +"eds.conll_dict2doc" = "edsnlp.data.converters:ConllDict2DocConverter" +"eds.omop_dict2doc" = "edsnlp.data.converters:OmopDict2DocConverter" +"eds.omop_doc2dict" = "edsnlp.data.converters:OmopDoc2DictConverter" +"eds.ents_doc2dict" = "edsnlp.data.converters:EntsDoc2DictConverter" + +# Deprecated (links to the same factories as above) +"SOFA" = "edsnlp.pipes.ner.scores.sofa.factory:create_component" +"accents" = "edsnlp.pipes.core.normalizer.accents.factory:create_component" +"charlson" = "edsnlp.pipes.ner.scores.charlson.factory:create_component" +"consultation_dates" = "edsnlp.pipes.misc.consultation_dates.factory:create_component" +"contextual-matcher" = "edsnlp.pipes.core.contextual_matcher.factory:create_component" +"eds.contextual-matcher" = "edsnlp.pipes.core.contextual_matcher.factory:create_component" +"dates" = "edsnlp.pipes.misc.dates.factory:create_component" +"eds.AIDS" = "edsnlp.pipes.ner.disorders.aids.factory:create_component" +"eds.CKD" = "edsnlp.pipes.ner.disorders.ckd.factory:create_component" +"eds.COPD" = "edsnlp.pipes.ner.disorders.copd.factory:create_component" +"eds.SOFA" = "edsnlp.pipes.ner.scores.sofa.factory:create_component" +"eds.TNM" = "edsnlp.pipes.ner.tnm.factory:create_component" +"eds.elston-ellis" = "edsnlp.pipes.ner.scores.elston_ellis.factory:create_component" +"eds.elstonellis" = "edsnlp.pipes.ner.scores.elston_ellis.factory:create_component" +"eds.emergency.ccmu" = "edsnlp.pipes.ner.scores.emergency.ccmu.factory:create_component" +"eds.emergency.gemsa" = "edsnlp.pipes.ner.scores.emergency.gemsa.factory:create_component" +"eds.emergency.priority" = "edsnlp.pipes.ner.scores.emergency.priority.factory:create_component" +"eds.measures" = "edsnlp.pipes.misc.quantities.factory:create_component" +"eds.measurements" = "edsnlp.pipes.misc.quantities.factory:create_component" +"eds.remove-lowercase" = "edsnlp.pipes.core.normalizer.remove_lowercase.factory:create_component" +"emergency.ccmu" = "edsnlp.pipes.ner.scores.emergency.ccmu.factory:create_component" +"emergency.gemsa" = "edsnlp.pipes.ner.scores.emergency.gemsa.factory:create_component" +"emergency.priority" = "edsnlp.pipes.ner.scores.emergency.priority.factory:create_component" +"endlines" = "edsnlp.pipes.core.endlines.factory:create_component" +"family" = "edsnlp.pipes.qualifiers.family.factory:create_component" +"hypothesis" = "edsnlp.pipes.qualifiers.hypothesis.factory:create_component" +"matcher" = "edsnlp.pipes.core.matcher.factory:create_component" +"negation" = "edsnlp.pipes.qualifiers.negation.factory:create_component" +"normalizer" = "edsnlp.pipes.core.normalizer.factory:create_component" +"pollution" = "edsnlp.pipes.core.normalizer.pollution.factory:create_component" +"quotes" = "edsnlp.pipes.core.normalizer.quotes.factory:create_component" +"reason" = "edsnlp.pipes.misc.reason.factory:create_component" +"remove-lowercase" = "edsnlp.pipes.core.normalizer.remove_lowercase.factory:create_component" +"reported_speech" = "edsnlp.pipes.qualifiers.reported_speech.factory:create_component" +"rspeech" = "edsnlp.pipes.qualifiers.reported_speech.factory:create_component" +"score" = "edsnlp.pipes.ner.scores.factory:create_component" +"sections" = "edsnlp.pipes.misc.sections.factory:create_component" +"sentences" = "edsnlp.pipes.core.sentences.factory:create_component" +"spaces" = "edsnlp.pipes.core.normalizer.spaces.factory:create_component" +"tables" = "edsnlp.pipes.misc.tables.factory:create_component" +"terminology" = "edsnlp.pipes.core.terminology.factory:create_component" + +# We could use spacy_factories in principle, but spacy auto-imports all entry points +# by default, when some of ours require optional dependencies (on the other hand, +# edsnlp only import an entrypoint if it is requested). By splitting our pipes between +# spacy_factories and edsnlp_factories, spacy will only look in the dict above and +# edsnlp will look both in the above dict and in the one below. +[project.entry-points."edsnlp_factories"] +# Trainable +"eds.transformer" = "edsnlp.pipes.trainable.embeddings.transformer.factory:create_component" +"eds.text_cnn" = "edsnlp.pipes.trainable.embeddings.text_cnn.factory:create_component" +"eds.span_pooler" = "edsnlp.pipes.trainable.embeddings.span_pooler.factory:create_component" +"eds.ner_crf" = "edsnlp.pipes.trainable.ner_crf.factory:create_component" +"eds.extractive_qa" = "edsnlp.pipes.trainable.extractive_qa.factory:create_component" +"eds.nested_ner" = "edsnlp.pipes.trainable.ner_crf.factory:create_component" +"eds.span_qualifier" = "edsnlp.pipes.trainable.span_classifier.factory:create_component" +"eds.span_classifier" = "edsnlp.pipes.trainable.span_classifier.factory:create_component" +"eds.span_linker" = "edsnlp.pipes.trainable.span_linker.factory:create_component" +"eds.biaffine_dep_parser" = "edsnlp.pipes.trainable.biaffine_dep_parser.factory:create_component" + +[project.entry-points."spacy_scorers"] +"eds.ner_exact" = "edsnlp.metrics.ner:NerExactMetric" +"eds.ner_token" = "edsnlp.metrics.ner:NerTokenMetric" +"eds.ner_overlap" = "edsnlp.metrics.ner:NerOverlapMetric" +"eds.span_attributes" = "edsnlp.metrics.span_attributes:SpanAttributeMetric" +"eds.dep_parsing" = "edsnlp.metrics.dep_parsing:DependencyParsingMetric" + +# Deprecated +"eds.ner_exact_metric" = "edsnlp.metrics.ner:NerExactMetric" +"eds.ner_token_metric" = "edsnlp.metrics.ner:NerTokenMetric" +"eds.ner_overlap_metric" = "edsnlp.metrics.ner:NerOverlapMetric" +"eds.span_attributes_metric" = "edsnlp.metrics.span_attributes:SpanAttributeMetric" +"eds.ner_exact_scorer" = "edsnlp.metrics.ner:NerExactMetric" +"eds.ner_token_scorer" = "edsnlp.metrics.ner:NerTokenMetric" +"eds.ner_overlap_scorer" = "edsnlp.metrics.ner:NerOverlapMetric" +"eds.span_attributes_scorer" = "edsnlp.metrics.span_attributes:SpanAttributeMetric" + +[project.entry-points."edsnlp_readers"] +"spark" = "edsnlp.data:from_spark" +"pandas" = "edsnlp.data:from_pandas" +"json" = "edsnlp.data:read_json" +"parquet" = "edsnlp.data:read_parquet" +"standoff" = "edsnlp.data:read_standoff" +"brat" = "edsnlp.data:read_brat" # alias for standoff +"conll" = "edsnlp.data:read_conll" + +[project.entry-points."edsnlp_writers"] +"spark" = "edsnlp.data:to_spark" +"pandas" = "edsnlp.data:to_pandas" +"json" = "edsnlp.data:write_json" +"standoff" = "edsnlp.data:write_standoff" +"brat" = "edsnlp.data:write_brat" # alias for standoff + +[project.entry-points."spacy_misc"] +"eds.span_context_getter" = "edsnlp.utils.span_getters:make_span_context_getter" + +[project.entry-points."spacy_languages"] +"eds" = "edsnlp.language:EDSLanguage" + +[project.entry-points."spacy_tokenizers"] +"eds.tokenizer" = "edsnlp.language:create_eds_tokenizer" + +[project.entry-points."mkdocs.plugins"] +"bibtex" = "docs.scripts.bibtex:BibTexPlugin" +"autorefs" = "docs.scripts.autorefs.plugin:AutorefsPlugin" +"clickable_snippets" = "docs.scripts.clickable_snippets:ClickableSnippetsPlugin" + +[build-system] +requires = [ + "setuptools", + "cython>=0.25", + "spacy>=3.2,!=3.8.2; python_version<'3.9'", + "spacy>=3.2,!=3.8.2,<4.0.0; python_version>='3.9'", + # thinc doesn't provide binaries for python<3.9 from 8.2.5 so we need to cap it ourselves + "thinc<8.2.5; python_version<'3.9'", + "thinc>=8.2.5; python_version>='3.9'", + # to update from https://github.com/scipy/oldest-supported-numpy/blob/main/setup.cfg + # while setting numpy >= 1.15.0 due to spacy reqs + "numpy==1.15.0; python_version=='3.7' and platform_machine not in 'arm64|aarch64|loongarch64' and platform_system!='AIX' and platform_python_implementation != 'PyPy'", + "numpy==1.15.0; python_version=='3.7' and platform_machine=='arm64' and platform_system=='Windows' and platform_python_implementation != 'PyPy'", + "numpy==1.16.0; python_version=='3.7' and platform_system=='AIX' and platform_machine!='loongarch64' and platform_python_implementation != 'PyPy'", + "numpy==1.17.3; python_version=='3.8' and platform_machine not in 'arm64|aarch64|s390x|loongarch64' and platform_python_implementation != 'PyPy'", + "numpy==1.17.3; python_version=='3.8' and platform_machine=='arm64' and platform_system=='Windows' and platform_python_implementation != 'PyPy'", + "numpy==1.17.5; python_version=='3.8' and platform_machine=='s390x' and platform_python_implementation != 'PyPy'", + "numpy==1.19.0; python_version=='3.6' and platform_machine!='loongarch64' and platform_python_implementation=='PyPy'", + "numpy==1.19.2; python_version=='3.7' and platform_machine=='aarch64' and platform_system!='AIX' and platform_python_implementation != 'PyPy'", + "numpy==1.19.2; python_version=='3.8' and platform_machine=='aarch64' and platform_python_implementation != 'PyPy'", + "numpy==1.20.0; python_version=='3.7' and platform_machine!='loongarch64' and platform_python_implementation=='PyPy'", + "numpy==1.21.0; python_version=='3.7' and platform_machine=='arm64' and platform_system=='Darwin' and platform_python_implementation!='PyPy'", + "numpy==1.21.0; python_version=='3.8' and platform_machine=='arm64' and platform_system=='Darwin' and platform_python_implementation!='PyPy'", + "numpy==1.22.2; python_version>='3.8' and python_version<'3.9' and platform_machine=='loongarch64' and platform_python_implementation!='PyPy'", + "numpy==1.22.2; python_version=='3.8' and platform_machine!='loongarch64' and platform_python_implementation=='PyPy'", + "numpy>=2.0; python_version>='3.9'", + "blis<1.0.0; python_version<'3.9'", + "blis<1.2.1; python_version>='3.9' and python_version<'3.10'", +] +build-backend = "setuptools.build_meta" + +[tool.ruff] +fix = true +extend-exclude = [ + ".git", + "__pycache__", + "__init__.py", + ".mypy_cache", + ".pytest_cache", + ".venv", + "build", + "edsnlp/pipes/factories.py", +] +line-length = 88 + +[tool.ruff.lint] +select = [ + "E", + "F", + "W", + "I001" +] + +[tool.ruff.lint.flake8-tidy-imports] +ban-relative-imports = "parents" + +[tool.ruff.lint.extend-per-file-ignores] +"__init__.py" = ["F401"] +"edsnlp/pipes/factories.py" = [ "F401", "E501" ] + +[tool.ruff.lint.isort] +known-first-party = ["edsnlp"] +known-third-party = ["build"] +order-by-type = true + +[tool.interrogate] +ignore-init-method = true +ignore-init-module = true +ignore-magic = false +ignore-semiprivate = true +ignore-private = true +ignore-property-decorators = true +ignore-module = true +ignore-nested-functions = true +ignore-nested-classes = true +ignore-setters = true +fail-under = 40 +exclude = ["setup.py", "docs", "build", "tests", "edsnlp/pipes/core/contextual_matcher/models.py"] +verbose = 0 +quiet = false +whitelist-regex = [] +ignore-regex = ['__(?!init).*__'] +color = true +omit-covered-files = false +# generate-badge = "." +# badge-format = "svg" + +[tool.pytest.ini_options] +# Some tests may download large objects such as the UMLS +# This timeout is mostly here to kill the CI in the case of deadlocks or infinite loops +timeout = 600 + +[tool.coverage.report] +precision = 2 +include = ["edsnlp/*"] +omit = [ + "tests/*", +] +exclude_lines = [ + "def __repr__", + "if __name__ == .__main__.:", + "@overload", + "pragma: no cover", + "raise .*Error", + "raise .*Exception", + "warn\\(", + "if __name__ == .__main__.:", + "if repr_id in exclude:", + "if TYPE_CHECKING:", + "class .*\\bProtocol\\):", + "@(abc\\.)?abstractmethod", + "Span.set_extension.*", + "Doc.set_extension.*", + "Token.set_extension.*", +] + +[tool.coverage.run] +include = ["edsnlp/*"] +concurrency = ["multiprocessing", "thread"] +parallel = true + +[tool.uv.pip] +torch-backend = "auto" + +[tool.cibuildwheel] +skip = [ + "*p36-*", # Skip Python 3.6 + "pp*", # Skip PyPy + "*-win32", # Skip 32-bit Windows + "*-manylinux_i686", # Skip 32-bit Linux + "*-win_arm64", # Skip experimental Windows on ARM + "*-musllinux*", # Skip slow Linux + "*-manylinux_ppc64le", # Skip slow Linux + "*-manylinux_s390x", # Skip slow Linux + "*p313-*", # skip 3.13 for now (spacy build issue): users will have to build it on their own +] + +before-test = 'pip install pytest "urllib3<2"' +test-extras = "ml" +test-command = "pytest {project}/tests/pipelines/test_pipelines.py"