[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
# Papyrus Structure Pipeline
Papyrus protocols used to standardize molecules. First used in Papyrus 05.6
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.7377161.svg)](https://doi.org/10.5281/zenodo.7377161).
## Installation
From source:
git clone https://github.com/OlivierBeq/Papyrus_Structure_Pipeline.git
pip install ./Papyrus_Structure_Pipeline
with pip:
```bash
pip install papyrus-structure-pipeline
```
## Usage
### Standardize a compound
Comparison to the [ChEMBL Structure Pipeline](https://github.com/chembl/ChEMBL_Structure_Pipeline):
```python
from rdkit import Chem
from chembl_structure_pipeline import standardizer as ChEMBL_standardizer
from papyrus_structure_pipeline import standardizer as Papyrus_standardizer
# CHEMBL1560279
smiles = "CCN(CC)C(=O)[n+]1ccc(OC)cc1.c1ccc([B-](c2ccccc2)(c2ccccc2)c2ccccc2)cc1"
mol = Chem.MolFromSmiles(smiles)
out1 = ChEMBL_standardizer.standardize_mol(mol)
out2 = Papyrus_standardizer.standardize(mol)
print(Chem.MolToSmiles(out1))
# CCN(CC)C(=O)[n+]1ccc(OC)cc1.c1ccc([B-](c2ccccc2)(c2ccccc2)c2ccccc2)cc1
print(Chem.MolToSmiles(out2))
# CCN(CC)C(=O)[n+]1ccc(OC)cc1
```
Get details on the standardization to identify why it fails for some molecules:
```python
smiles_list = [
# erlotinib
"n1cnc(c2cc(c(cc12)OCCOC)OCCOC)Nc1cc(ccc1)C#C",
# midecamycin
"CCC(=O)O[C@@H]1CC(=O)O[C@@H](C/C=C/C=C/[C@@H]([C@@H](C[C@@H]([C@@H]([C@H]1OC)O[C@H]2[C@@H]([C@H]([C@@H]([C@H](O2)C)O[C@H]3C[C@@]([C@H]([C@@H](O3)C)OC(=O)CC)(C)O)N(C)C)O)CC=O)C)O)C",
# selenofolate
"C1=CC(=CC=C1C(=O)NC(CCC(=O)OCC[Se]C#N)C(=O)O)NCC2=CN=C3C(=N2)C(=O)NC(=N3)N",
# cisplatin
"N.N.Cl[Pt]Cl"
]
for smiles in smiles_list:
mol = Chem.MolFromSmiles(smiles)
print(Papyrus_standardizer.standardize(mol, return_type=True))
# (<rdkit.Chem.rdchem.Mol object at 0x000000946F99B580>, <StandardizationResult.CORRECT_MOLECULE: 1>)
# (None, <StandardizationResult.NON_SMALL_MOLECULE: 2>)
# (None, <StandardizationResult.INORGANIC_MOLECULE: 3>)
# (None, <StandardizationResult.MIXTURE_MOLECULE: 4>)
```
Allow other atoms to be considered organic:
```python
smiles = "CCN(CC)C(=O)C1=CC=C(S1)C2=C3C=CC(=[N+](C)C)C=C3[Se]C4=C2C=CC(=C4)N(C)C.F[P-](F)(F)(F)(F)F"
mol = Chem.MolFromSmiles(smiles)
print(Papyrus_standardizer.standardize(mol, return_type=True))
# (None, <StandardizationResult.INORGANIC_MOLECULE: 3>)
Papyrus_standardizer.ORGANIC_ATOMS.append('Se')
print(Papyrus_standardizer.standardize(mol, return_type=True))
# (<rdkit.Chem.rdchem.Mol object at 0x0000009F24D15F90>, <StandardizationResult.CORRECT_MOLECULE: 1>)
Papyrus_standardizer.ORGANIC_ATOMS = Papyrus_standardizer.ORGANIC_ATOMS[:-1]
print(Papyrus_standardizer.standardize(mol, return_type=True))
# (None, <StandardizationResult.INORGANIC_MOLECULE: 3>)
```
Add custom substructures to be removed as salts:
```python
# lomitapide
smiles = "C1CN(CCC1NC(=O)C2=CC=CC=C2C3=CC=C(C=C3)C(F)(F)F)CCCCC4(C5=CC=CC=C5C6=CC=CC=C64)C(=O)NCC(F)(F)F.c1ccccc1"
mol = Chem.MolFromSmiles(smiles)
print(Papyrus_standardizer.standardize(mol, return_type=True))
# (None, <StandardizationResult.MIXTURE_MOLECULE: 4>)
Papyrus_standardizer.SALTS.append('c1ccccc1')
print(Papyrus_standardizer.standardize(mol, return_type=True))
# (<rdkit.Chem.rdchem.Mol object at 0x0000009F24D15F90>, <StandardizationResult.CORRECT_MOLECULE: 1>)
Papyrus_standardizer.SALTS = Papyrus_standardizer.SALTS[:-1]
print(Papyrus_standardizer.standardize(mol, return_type=True))
# (None, <StandardizationResult.MIXTURE_MOLECULE: 4>)
```
## Documentation
```python
def standardize(mol,
remove_additional_salts=True, remove_additional_metals=True,
filter_mixtures=True, filter_inorganic=True, filter_non_small_molecule=True,
canonicalize_tautomer=True, small_molecule_min_mw=200, small_molecule_max_mw=800,
tautomer_allow_stereo_removal=True, tautomer_max_tautomers=0, return_type=False,
raise_error=True
) -> Chem.Mol:
```
#### Parameters
- ***mol : Chem.Mol***
RDKit molecule object to standardize.
- ***remove_additional_salts : bool***
Removes a custom set of fragments if present in the molecule object.
- ***remove_additional_metals : bool***
Removes metal fragments if present in the molecule object.<br/>Ignored if `remove_additional_salts` is set to `False`.
- ***filter_mixtures : bool***
Return `None` if the molecule is a mixture.
- ***filter_inorganic : bool***
Return `None` if the molecule is inorganic.
- ***filter_non_small_molecule : bool***
Return `None` if the molecule is not a small molecule.
- ***canonicalize_tautomer : bool***
Canonicalize the tautomeric state of the molecule.
- ***small_molecule_min_mw : float***
Molecular weight under which a molecule is considered too small.
- ***small_molecule_max_mw : float***
Molecular weight above which a molecule is considered too big.
- ***tautomer_allow_stereo_removal : bool***
Allow the tautomer search algorithm to remove stereocenters.
- ***tautomer_max_tautomers : int (< 2 <sup>32</sup>)***
Maximum number of tautomers to consider by the tautomer search algorithm.
- ***return_type : bool***
Add a StandardizationResult to the return value.
- ***raise_error : bool***
Raise an exception upon failure, otherwise return None
________________
```python
def is_organic(mol, return_type=False) -> bool:
```
Return whether the RKDit molecule is organic or not.
- Makes internal use of the variable `ORGANIC_ATOMS`
#### Parameters
- ***mol : Chem.Mol***
RDKit molecule object to check the organic nature of.
- ***return_type : bool***
Add a InorganicSubtype to the return value.
________________
```python
def is_small_molecule(mol,
min_molwt=200,
max_molwt=800
) -> bool:
```
Return whether the RKDit molecule has a molecular weight within `min_molwt` and `max_molwt`.
#### Parameters
- ***mol : Chem.Mol***
RDKit molecule object to check the organic nature of.
- ***min_molwt : float***
Molecular weight under which a molecule is considered too small.
- ***max_molwt : float***
Molecular weight above which a molecule is considered too big.
________________
```python
def is_mixture(mol) -> bool:
```
Return whether the RKDit molecule is composed of multiple fragments.
#### Parameters
- ***mol : Chem.Mol***
RDKit molecule object to check the organic nature of.
________________
```python
def _apply_chembl_standardization(mol) -> Chem.Mol:
```
Apply the ChEMBL structure standardization pipeline on a RDKit molecule.
- Makes use of both `ChEMBL_standardizer.get_parent_mol` and `ChEMBL_standardizer.standardize_mol`.
- Ensures the obtained SMILES can be parsed by the RDKit.
#### Parameters
- ***mol : Chem.Mol***
RDKit molecule object to apply the ChEMBL Structure Pipeline to.
________________
```python
def _canonicalize_tautomer(mol,
allow_stereo_removal=True, max_tautomers=2 ** 32 - 1
) -> Chem.Mol:
```
Obtain the RDKit canonical tautomer of the given RDKit molecule.
- Makes use of both `ChEMBL_standardizer.get_parent_mol` and `ChEMBL_standardizer.standardize_mol`.
- Ensures the obtained SMILES can be parsed by the RDKit.
#### Parameters
- ***mol : Chem.Mol***
RDKit molecule object to RDKit canonical tautomer of.
- ***allow_stereo_removal : bool***
Allow the tautomer search algorithm to remove stereocenters.
- ***max_tautomers : int (<2 <sup>32</sup>)***
Maximum number of tautomers to consider by the tautomer search algorithm.
________________
```python
def _remove_supplementary_salts(mol,
include_metals=True, return_type=False
) -> Chem.Mol:
```
Remove substructures (e.g. salts) not dealt with by the ChEMBL pipeline structure.
The additional substructures are defined by the `SALTS` variable.
#### Parameters
- ***mol : Chem.Mol***
RDKit molecule object from which to remove additional substructures.
- ***include_metals : bool***
Removes metal fragments if present in the molecule object (defined by the `METALS` variable).
- ***return_type : bool***
Add a SaltStrippingResult to the return value.
Raw data
{
"_id": null,
"home_page": "https://github.com/OlivierBeq/Papyrus_structure_pipeline",
"name": "papyrus-structure-pipeline",
"maintainer": "Olivier J. M. B\u00e9quignon",
"docs_url": null,
"requires_python": "",
"maintainer_email": "\"olivier.bequignon.maintainer@gmail.com\"",
"keywords": "cheminformatics,molecule,standardization",
"author": "Olivier J. M. B\u00e9quignon",
"author_email": "\"olivier.bequignon.maintainer@gmail.com\"",
"download_url": "https://files.pythonhosted.org/packages/ab/f4/416f79fb4bc42f816ff8dcbb65e98ef11160c6c7805a44c7e16b53956331/papyrus_structure_pipeline-0.0.5.tar.gz",
"platform": null,
"description": "\r\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\r\n\r\n# Papyrus Structure Pipeline\r\n\r\nPapyrus protocols used to standardize molecules. First used in Papyrus 05.6\r\n[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.7377161.svg)](https://doi.org/10.5281/zenodo.7377161).\r\n\r\n## Installation\r\n\r\nFrom source:\r\n\r\n git clone https://github.com/OlivierBeq/Papyrus_Structure_Pipeline.git\r\n pip install ./Papyrus_Structure_Pipeline\r\n\r\nwith pip:\r\n\r\n```bash\r\npip install papyrus-structure-pipeline\r\n```\r\n\r\n## Usage\r\n\r\n### Standardize a compound\r\n\r\nComparison to the [ChEMBL Structure Pipeline](https://github.com/chembl/ChEMBL_Structure_Pipeline):\r\n\r\n```python\r\nfrom rdkit import Chem\r\nfrom chembl_structure_pipeline import standardizer as ChEMBL_standardizer\r\nfrom papyrus_structure_pipeline import standardizer as Papyrus_standardizer\r\n\r\n# CHEMBL1560279\r\nsmiles = \"CCN(CC)C(=O)[n+]1ccc(OC)cc1.c1ccc([B-](c2ccccc2)(c2ccccc2)c2ccccc2)cc1\"\r\n\r\nmol = Chem.MolFromSmiles(smiles)\r\nout1 = ChEMBL_standardizer.standardize_mol(mol)\r\nout2 = Papyrus_standardizer.standardize(mol)\r\n\r\n\r\nprint(Chem.MolToSmiles(out1))\r\n# CCN(CC)C(=O)[n+]1ccc(OC)cc1.c1ccc([B-](c2ccccc2)(c2ccccc2)c2ccccc2)cc1\r\n\r\nprint(Chem.MolToSmiles(out2))\r\n# CCN(CC)C(=O)[n+]1ccc(OC)cc1\r\n```\r\n\r\nGet details on the standardization to identify why it fails for some molecules:\r\n\r\n```python\r\nsmiles_list = [\r\n # erlotinib\r\n \"n1cnc(c2cc(c(cc12)OCCOC)OCCOC)Nc1cc(ccc1)C#C\",\r\n # midecamycin\r\n \"CCC(=O)O[C@@H]1CC(=O)O[C@@H](C/C=C/C=C/[C@@H]([C@@H](C[C@@H]([C@@H]([C@H]1OC)O[C@H]2[C@@H]([C@H]([C@@H]([C@H](O2)C)O[C@H]3C[C@@]([C@H]([C@@H](O3)C)OC(=O)CC)(C)O)N(C)C)O)CC=O)C)O)C\",\r\n # selenofolate\r\n \"C1=CC(=CC=C1C(=O)NC(CCC(=O)OCC[Se]C#N)C(=O)O)NCC2=CN=C3C(=N2)C(=O)NC(=N3)N\",\r\n # cisplatin\r\n \"N.N.Cl[Pt]Cl\"\r\n]\r\n\r\nfor smiles in smiles_list:\r\n mol = Chem.MolFromSmiles(smiles)\r\n print(Papyrus_standardizer.standardize(mol, return_type=True))\r\n\r\n \r\n# (<rdkit.Chem.rdchem.Mol object at 0x000000946F99B580>, <StandardizationResult.CORRECT_MOLECULE: 1>)\r\n# (None, <StandardizationResult.NON_SMALL_MOLECULE: 2>)\r\n# (None, <StandardizationResult.INORGANIC_MOLECULE: 3>)\r\n# (None, <StandardizationResult.MIXTURE_MOLECULE: 4>)\r\n```\r\n\r\nAllow other atoms to be considered organic:\r\n\r\n```python\r\nsmiles = \"CCN(CC)C(=O)C1=CC=C(S1)C2=C3C=CC(=[N+](C)C)C=C3[Se]C4=C2C=CC(=C4)N(C)C.F[P-](F)(F)(F)(F)F\"\r\nmol = Chem.MolFromSmiles(smiles)\r\n\r\nprint(Papyrus_standardizer.standardize(mol, return_type=True))\r\n# (None, <StandardizationResult.INORGANIC_MOLECULE: 3>)\r\n\r\nPapyrus_standardizer.ORGANIC_ATOMS.append('Se')\r\n\r\nprint(Papyrus_standardizer.standardize(mol, return_type=True))\r\n# (<rdkit.Chem.rdchem.Mol object at 0x0000009F24D15F90>, <StandardizationResult.CORRECT_MOLECULE: 1>)\r\n\r\nPapyrus_standardizer.ORGANIC_ATOMS = Papyrus_standardizer.ORGANIC_ATOMS[:-1]\r\n\r\nprint(Papyrus_standardizer.standardize(mol, return_type=True))\r\n# (None, <StandardizationResult.INORGANIC_MOLECULE: 3>)\r\n```\r\n\r\nAdd custom substructures to be removed as salts:\r\n\r\n```python\r\n# lomitapide\r\nsmiles = \"C1CN(CCC1NC(=O)C2=CC=CC=C2C3=CC=C(C=C3)C(F)(F)F)CCCCC4(C5=CC=CC=C5C6=CC=CC=C64)C(=O)NCC(F)(F)F.c1ccccc1\"\r\nmol = Chem.MolFromSmiles(smiles)\r\n\r\nprint(Papyrus_standardizer.standardize(mol, return_type=True))\r\n# (None, <StandardizationResult.MIXTURE_MOLECULE: 4>)\r\n\r\nPapyrus_standardizer.SALTS.append('c1ccccc1')\r\n\r\nprint(Papyrus_standardizer.standardize(mol, return_type=True))\r\n# (<rdkit.Chem.rdchem.Mol object at 0x0000009F24D15F90>, <StandardizationResult.CORRECT_MOLECULE: 1>)\r\n\r\nPapyrus_standardizer.SALTS = Papyrus_standardizer.SALTS[:-1]\r\n\r\nprint(Papyrus_standardizer.standardize(mol, return_type=True))\r\n# (None, <StandardizationResult.MIXTURE_MOLECULE: 4>)\r\n```\r\n\r\n\r\n## Documentation\r\n\r\n\r\n```python\r\ndef standardize(mol,\r\n remove_additional_salts=True, remove_additional_metals=True,\r\n filter_mixtures=True, filter_inorganic=True, filter_non_small_molecule=True,\r\n canonicalize_tautomer=True, small_molecule_min_mw=200, small_molecule_max_mw=800,\r\n tautomer_allow_stereo_removal=True, tautomer_max_tautomers=0, return_type=False,\r\n raise_error=True\r\n ) -> Chem.Mol:\r\n```\r\n\r\n#### Parameters\r\n\r\n- ***mol : Chem.Mol*** \r\n RDKit molecule object to standardize.\r\n- ***remove_additional_salts : bool*** \r\n Removes a custom set of fragments if present in the molecule object.\r\n- ***remove_additional_metals : bool*** \r\n Removes metal fragments if present in the molecule object.<br/>Ignored if `remove_additional_salts` is set to `False`.\r\n- ***filter_mixtures : bool*** \r\n Return `None` if the molecule is a mixture. \r\n- ***filter_inorganic : bool*** \r\n Return `None` if the molecule is inorganic.\r\n- ***filter_non_small_molecule : bool*** \r\n Return `None` if the molecule is not a small molecule.\r\n- ***canonicalize_tautomer : bool*** \r\n Canonicalize the tautomeric state of the molecule.\r\n- ***small_molecule_min_mw : float*** \r\n Molecular weight under which a molecule is considered too small. \r\n- ***small_molecule_max_mw : float*** \r\n Molecular weight above which a molecule is considered too big.\r\n- ***tautomer_allow_stereo_removal : bool*** \r\n Allow the tautomer search algorithm to remove stereocenters. \r\n- ***tautomer_max_tautomers : int (< 2 <sup>32</sup>)*** \r\n Maximum number of tautomers to consider by the tautomer search algorithm.\r\n- ***return_type : bool*** \r\n Add a StandardizationResult to the return value.\r\n- ***raise_error : bool***\r\n Raise an exception upon failure, otherwise return None\r\n\r\n________________\r\n\r\n```python\r\ndef is_organic(mol, return_type=False) -> bool:\r\n```\r\n\r\nReturn whether the RKDit molecule is organic or not.\r\n - Makes internal use of the variable `ORGANIC_ATOMS` \r\n\r\n#### Parameters\r\n\r\n- ***mol : Chem.Mol*** \r\n RDKit molecule object to check the organic nature of.\r\n- ***return_type : bool*** \r\n Add a InorganicSubtype to the return value. \r\n\r\n________________\r\n\r\n```python\r\ndef is_small_molecule(mol,\r\n min_molwt=200,\r\n max_molwt=800\r\n ) -> bool:\r\n```\r\n\r\nReturn whether the RKDit molecule has a molecular weight within `min_molwt` and `max_molwt`.\r\n\r\n#### Parameters\r\n\r\n- ***mol : Chem.Mol*** \r\n RDKit molecule object to check the organic nature of.\r\n- ***min_molwt : float*** \r\n Molecular weight under which a molecule is considered too small. \r\n- ***max_molwt : float*** \r\n Molecular weight above which a molecule is considered too big.\r\n\r\n________________\r\n\r\n\r\n```python\r\ndef is_mixture(mol) -> bool:\r\n```\r\n\r\nReturn whether the RKDit molecule is composed of multiple fragments.\r\n\r\n#### Parameters\r\n\r\n- ***mol : Chem.Mol*** \r\n RDKit molecule object to check the organic nature of.\r\n\r\n________________\r\n\r\n```python\r\ndef _apply_chembl_standardization(mol) -> Chem.Mol:\r\n``` \r\n\r\nApply the ChEMBL structure standardization pipeline on a RDKit molecule.\r\n- Makes use of both `ChEMBL_standardizer.get_parent_mol` and `ChEMBL_standardizer.standardize_mol`.\r\n- Ensures the obtained SMILES can be parsed by the RDKit.\r\n\r\n#### Parameters\r\n\r\n- ***mol : Chem.Mol*** \r\n RDKit molecule object to apply the ChEMBL Structure Pipeline to.\r\n\r\n________________\r\n\r\n```python\r\ndef _canonicalize_tautomer(mol,\r\n allow_stereo_removal=True, max_tautomers=2 ** 32 - 1\r\n ) -> Chem.Mol:\r\n``` \r\n\r\nObtain the RDKit canonical tautomer of the given RDKit molecule.\r\n- Makes use of both `ChEMBL_standardizer.get_parent_mol` and `ChEMBL_standardizer.standardize_mol`.\r\n- Ensures the obtained SMILES can be parsed by the RDKit.\r\n\r\n#### Parameters\r\n\r\n- ***mol : Chem.Mol*** \r\n RDKit molecule object to RDKit canonical tautomer of.\r\n- ***allow_stereo_removal : bool*** \r\n Allow the tautomer search algorithm to remove stereocenters.\r\n- ***max_tautomers : int (<2 <sup>32</sup>)*** \r\n Maximum number of tautomers to consider by the tautomer search algorithm.\r\n\r\n________________\r\n\r\n```python\r\ndef _remove_supplementary_salts(mol,\r\n include_metals=True, return_type=False\r\n ) -> Chem.Mol:\r\n``` \r\n\r\nRemove substructures (e.g. salts) not dealt with by the ChEMBL pipeline structure.\r\n\r\nThe additional substructures are defined by the `SALTS` variable.\r\n\r\n#### Parameters\r\n\r\n- ***mol : Chem.Mol*** \r\n RDKit molecule object from which to remove additional substructures.\r\n- ***include_metals : bool*** \r\n Removes metal fragments if present in the molecule object (defined by the `METALS` variable).\r\n- ***return_type : bool*** \r\n Add a SaltStrippingResult to the return value.\r\n\r\n",
"bugtrack_url": null,
"license": "",
"summary": "Papyrus Structure Pipeline",
"version": "0.0.5",
"project_urls": {
"Homepage": "https://github.com/OlivierBeq/Papyrus_structure_pipeline"
},
"split_keywords": [
"cheminformatics",
"molecule",
"standardization"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "245c58fbb439a982994c1ef88e8a66041f720a1df6bcab8227e73594ca225041",
"md5": "5e7ccaa66a3b456e22660a14d8a934d6",
"sha256": "86fdcd17fb44a4d928369c10184acd31df321016e88bf6ca7111d546965fc4fd"
},
"downloads": -1,
"filename": "papyrus_structure_pipeline-0.0.5-py3-none-any.whl",
"has_sig": false,
"md5_digest": "5e7ccaa66a3b456e22660a14d8a934d6",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 9766,
"upload_time": "2023-09-20T10:51:32",
"upload_time_iso_8601": "2023-09-20T10:51:32.158527Z",
"url": "https://files.pythonhosted.org/packages/24/5c/58fbb439a982994c1ef88e8a66041f720a1df6bcab8227e73594ca225041/papyrus_structure_pipeline-0.0.5-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "abf4416f79fb4bc42f816ff8dcbb65e98ef11160c6c7805a44c7e16b53956331",
"md5": "679618967faa0fea37ae9562c6e26187",
"sha256": "443431e8d8a353cd50f44f9a28727a827723e1c4a241c558541fc528e36b3668"
},
"downloads": -1,
"filename": "papyrus_structure_pipeline-0.0.5.tar.gz",
"has_sig": false,
"md5_digest": "679618967faa0fea37ae9562c6e26187",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 11505,
"upload_time": "2023-09-20T10:51:33",
"upload_time_iso_8601": "2023-09-20T10:51:33.214334Z",
"url": "https://files.pythonhosted.org/packages/ab/f4/416f79fb4bc42f816ff8dcbb65e98ef11160c6c7805a44c7e16b53956331/papyrus_structure_pipeline-0.0.5.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-09-20 10:51:33",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "OlivierBeq",
"github_project": "Papyrus_structure_pipeline",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"tox": true,
"lcname": "papyrus-structure-pipeline"
}