Skip to content

Commit f38ae44

Browse files
committed
Use logging instead of print
1 parent 17b2abd commit f38ae44

6 files changed

Lines changed: 90 additions & 51 deletions

File tree

docs/source/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313

1414
### Changed
1515

16+
- Use logging instead of print.
1617
- Move from `.rst` to `.md` on the documentation.
1718
- Update installation instructions.
1819
- Versioning: `Major.Minor.Patch` --> `Major.Minnor.Patch.postX` it helps to distinguish commits that are not yet included on the `Patch`.

src/moldrug/cli.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
"""
88
import argparse
99
import datetime
10-
import inspect
1110
import importlib
11+
import inspect
1212
import os
1313
import sys
1414
from typing import Union
@@ -17,6 +17,7 @@
1717
from rdkit import Chem
1818

1919
from moldrug import __version__, constraintconf, utils
20+
from moldrug.logging_utils import log
2021

2122

2223
class CommandLineHelper:
@@ -131,7 +132,8 @@ def _translate_config(self):
131132
if 'type' not in MainConfig['cluster'] or 'kwargs' not in MainConfig['cluster']:
132133
raise ValueError("The cluster configuration must contain 'type' and 'kwargs'.")
133134

134-
from moldrug.runner import Runner, RunnerMode # terminates with a message if dask is not installed
135+
from moldrug.runner import ( # terminates with a message if dask is not installed
136+
Runner, RunnerMode)
135137

136138
try:
137139
cluster_class = getattr(importlib.import_module("dask_jobqueue"), MainConfig['cluster']['type'])
@@ -321,10 +323,9 @@ def __moldrug_cmd():
321323
)
322324
UserArgs = CommandLineHelper(parser)
323325

324-
print(
325-
f"Started at {datetime.datetime.now().strftime('%c')}\n"
326-
f"You are using moldrug: {__version__}.\n\n"
327-
f"{UserArgs}\n\n")
326+
log(f"Started at {datetime.datetime.now().strftime('%c')}")
327+
log(f"You are using moldrug: {__version__}\n")
328+
log(f"{UserArgs}\n\n")
328329

329330
# Call the class
330331
UserArgs.run_moldrugClass()
@@ -336,7 +337,7 @@ def __moldrug_cmd():
336337
if UserArgs.FollowConfig:
337338
MutableArgs = UserArgs.MutableArgs.copy()
338339
for job in UserArgs.FollowConfig:
339-
print(f"The follow job {job} started.")
340+
log(f"The follow job {job} started.")
340341

341342
# Updating arguments
342343
MutableArgs.update(UserArgs.FollowConfig[job])
@@ -350,7 +351,7 @@ def __moldrug_cmd():
350351
UserArgs.run_moldrugClass()
351352
# Saving data
352353
UserArgs.save_data()
353-
print(f'The job {job} finished!')
354+
log(f'The job {job} finished!')
354355

355356
# Clean checkpoint on normal end
356357
if os.path.isfile('cpt.pbz2'):
@@ -430,3 +431,4 @@ def __constraintconf_cmd():
430431

431432
if __name__ == '__main__':
432433
pass
434+
pass

src/moldrug/constraintconf.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
# import warnings
2929
from tqdm import tqdm
3030

31-
from moldrug import verbose
31+
from moldrug.logging_utils import LogLevel, log
3232
from moldrug.utils import compressed_pickle
3333

3434

@@ -190,10 +190,9 @@ def generate_conformers(mol: Chem.rdchem.Mol,
190190
try:
191191
AllChem.ConstrainedEmbed(temp_mol, core1, randomseed=i)
192192
except Exception as e:
193-
if verbose:
194-
print(f"AllChem.ConstrainedEmbed fails with: {e}. \n"
195-
f"On the molecules:\n current mol: {Chem.MolToSmiles(temp_mol)}\n"
196-
f"core: {Chem.MolToSmiles(core1)}\nTrying with gen_aligned_conf")
193+
log(f"AllChem.ConstrainedEmbed fails with: {e}. \n"
194+
f"On the molecule:\n current mol: {Chem.MolToSmiles(temp_mol)}\n"
195+
f"core: {Chem.MolToSmiles(core1)}\nTrying with gen_aligned_conf", LogLevel.DEBUG)
197196
temp_mol = gen_aligned_conf(temp_mol, ref_mol, ref_smi, randomseed=randomseed)
198197
# Remove the explicit Hs
199198
temp_mol = Chem.RemoveHs(temp_mol)

src/moldrug/fitness.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
from rdkit import Chem
1212
from rdkit.Chem import QED, Descriptors
1313

14-
from moldrug import constraintconf, utils, verbose
14+
from moldrug import constraintconf, utils
15+
from moldrug.logging_utils import log, LogLevel
1516

1617

1718
def __get_default_desirability(multireceptor: bool = False) -> dict:
@@ -352,8 +353,7 @@ def _vinadock(
352353
minimum_conf_rms=constraint_minimum_conf_rms,
353354
randomseed=vina_seed)
354355
except Exception as e:
355-
if verbose:
356-
print(f"constraintconf.generate_conformers fails inside moldrug.fitness._vinadock with {e}")
356+
log(f"constraintconf.generate_conformers fails inside moldrug.fitness._vinadock with {e}", LogLevel.DEBUG)
357357
vina_score_pdbqt = (np.inf, "NonValidConformer")
358358
return vina_score_pdbqt
359359
# Remove conformers that clash with the protein in case of score_only,
@@ -450,9 +450,8 @@ def _vinadock(
450450
}
451451
utils.compressed_pickle(f'error/{Individual.idx}_error', error)
452452
# warn(f"\nVina failed! Check: {Individual.idx}_error.pbz2 file in error.\n")
453-
if verbose:
454-
for key in error:
455-
print(f"{key}: {error[key]}")
453+
for key in error:
454+
log(f"{key}: {error[key]}", LogLevel.DEBUG)
456455
vina_score_pdbqt = (np.inf, 'VinaFailed')
457456
return vina_score_pdbqt
458457

src/moldrug/logging_utils.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import logging
2+
from enum import Enum
3+
from moldrug import verbose
4+
5+
6+
class LogLevel(Enum):
7+
DEBUG = logging.DEBUG
8+
INFO = logging.INFO
9+
WARNING = logging.WARNING
10+
ERROR = logging.ERROR
11+
CRITICAL = logging.CRITICAL
12+
13+
14+
# Create a logger
15+
logger = logging.getLogger("moldrug")
16+
17+
# Only configure if no handlers are set (prevents messing with user config)
18+
if not logger.hasHandlers():
19+
handler = logging.StreamHandler()
20+
formatter = logging.Formatter(
21+
"%(asctime)s - %(name)s - %(levelname)s - %(message)s"
22+
)
23+
handler.setFormatter(formatter)
24+
logger.addHandler(handler)
25+
# Set default level depending on verbose
26+
logger.setLevel(logging.DEBUG if verbose else logging.INFO)
27+
28+
29+
def log(msg: str, level: LogLevel = LogLevel.INFO):
30+
"""Unified logging function for the library."""
31+
logger.log(level.value, msg)

src/moldrug/utils.py

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
from copy import deepcopy
1313
from inspect import signature
1414
from typing import Callable, Dict, Iterable, List, Optional, Union
15-
from warnings import warn
1615

1716
import dill as pickle
1817
import numpy as np
@@ -24,7 +23,8 @@
2423
from rdkit.Chem import AllChem, DataStructs, Descriptors, Lipinski, rdFMCS
2524

2625
from moldrug import __version__
27-
from moldrug.runner import Runner, RunnerMode, parallel_execution_multiprocessing
26+
from moldrug.logging_utils import log, LogLevel
27+
from moldrug.runner import Runner, RunnerMode
2828

2929
RDLogger.DisableLog('rdApp.*')
3030
# # in order to pickle the isotope properties of the molecule
@@ -930,8 +930,11 @@ def make_sdf(individuals: List[Individual], sdf_name: str = 'out'):
930930
w.write(mol)
931931
except Exception:
932932
# Should be that the pdbqt is not valid
933-
print(f"{individual} does not have a valid pdbqt: {individual.pdbqt}.")
934-
print(f" File {sdf_name}_{i+1}.sdf was created!")
933+
log(
934+
f"{individual} does not have a valid pdbqt: {individual.pdbqt}.",
935+
LogLevel.ERROR
936+
)
937+
log(f"File {sdf_name}_{i+1}.sdf was created!")
935938
else:
936939
with Chem.SDWriter(f"{sdf_name}.sdf") as w:
937940
for individual in individuals:
@@ -949,8 +952,11 @@ def make_sdf(individuals: List[Individual], sdf_name: str = 'out'):
949952
w.write(mol)
950953
except Exception:
951954
# Should be that the pdbqt is not valid
952-
print(f"{individual} does not have a valid pdbqt: {individual.pdbqt}.")
953-
print(f"File {sdf_name}.sdf was createad!")
955+
log(
956+
f"{individual} does not have a valid pdbqt: {individual.pdbqt}.",
957+
LogLevel.ERROR
958+
)
959+
log(f"File {sdf_name}_{i+1}.sdf was created!")
954960

955961

956962
def _make_kwargs_copy(costfunc, costfunc_kwargs,):
@@ -982,9 +988,10 @@ def tar_errors(error_path: str = 'error'):
982988
if os.path.isdir(error_path):
983989
if os.listdir(error_path):
984990
shutil.make_archive('error', 'gztar', error_path)
985-
print(f"\n{50*'=+'}")
986-
print("Note: Check the running warnings and erorrs in error.tar.gz file!")
987-
print(f"{50*'=+'}\n")
991+
992+
log(f"\t\t{20*'=+'}")
993+
log("Check the running warnings and erorrs in error.tar.gz file!", LogLevel.WARNING)
994+
log(f"\t\t{20*'=+'}")
988995
shutil.rmtree(error_path)
989996

990997
######################
@@ -1147,8 +1154,8 @@ def __call__(self, njobs: int = 1, pick: int = None, runner: Optional[Runner] =
11471154

11481155
# Check version of moldrug
11491156
if self.__moldrug_version != __version__:
1150-
warn(f"{self.__class__.__name__} was initilized with moldrug-{self.__moldrug_version} "
1151-
f"but was called with moldrug-{__version__}")
1157+
log(f"{self.__class__.__name__} was initilized with moldrug-{self.__moldrug_version} "
1158+
f"but was called with moldrug-{__version__}", LogLevel.ERROR)
11521159
self.grow_crem_kwargs.update({'return_mol': True})
11531160
new_mols = list(grow_mol(self._seed_mol, self.crem_db_path, **self.grow_crem_kwargs))
11541161
if pick:
@@ -1171,7 +1178,7 @@ def __call__(self, njobs: int = 1, pick: int = None, runner: Optional[Runner] =
11711178
for individual in self.pop:
11721179
args_list.append((individual, kwargs_copy))
11731180

1174-
print('Calculating cost function...')
1181+
log('Calculating cost function...')
11751182
self.pop = runner.run(self.__costfunc__, args_list)
11761183

11771184
# Clean directory
@@ -1180,7 +1187,7 @@ def __call__(self, njobs: int = 1, pick: int = None, runner: Optional[Runner] =
11801187
tar_errors('error')
11811188

11821189
# Printing how long was the simulation
1183-
print(f"Finished at {datetime.datetime.now().strftime('%c')}.\n")
1190+
log(f"Finished at {datetime.datetime.now().strftime('%c')}.\n")
11841191

11851192
def __costfunc__(self, args_list):
11861193
Individual, kwargs = args_list
@@ -1442,8 +1449,8 @@ def __call__(self, njobs: int = 1, runner: Optional[Runner] = None):
14421449

14431450
# Check version of moldrug
14441451
if self.__moldrug_version__ != __version__:
1445-
warn(f"{self.__class__.__name__} was initialized with moldrug-{self.__moldrug_version__} "
1446-
f"but was called with moldrug-{__version__}")
1452+
log(f"{self.__class__.__name__} was initialized with moldrug-{self.__moldrug_version__} "
1453+
f"but was called with moldrug-{__version__}", LogLevel.ERROR)
14471454

14481455
# Here we will update if needed some parameters for
14491456
# the crem operations that could change between different calls.
@@ -1466,7 +1473,7 @@ def __call__(self, njobs: int = 1, runner: Optional[Runner] = None):
14661473
"generate any new molecule during the initialization of the population. "
14671474
"Check the provided crem parameters!")
14681475
if len(GenInitStructs) < (self.popsize - len(self._seed_mol)):
1469-
print('The initial population has repeated elements')
1476+
log('The initial population has repeated elements', LogLevel.WARNING)
14701477
# temporal solution
14711478
GenInitStructs += random.choices(GenInitStructs,
14721479
k=self.popsize - len(GenInitStructs) - len(self._seed_mol))
@@ -1506,7 +1513,7 @@ def __call__(self, njobs: int = 1, runner: Optional[Runner] = None):
15061513
for individual in self.pop:
15071514
args_list.append((individual, kwargs_copy))
15081515

1509-
print(f'\n\nCreating the first population with {len(self.pop)} members:')
1516+
log(f'Creating the first population with {len(self.pop)} members:')
15101517
self.pop = runner.run(self.__costfunc__, entries=args_list)
15111518

15121519
# Clean directory
@@ -1527,8 +1534,8 @@ def __call__(self, njobs: int = 1, runner: Optional[Runner] = None):
15271534
self.pop = sorted(self.pop, key=lambda x: x.idx)
15281535
self.pop = sorted(self.pop)
15291536
# Print some information of the initial population
1530-
print(f"Initial Population: Best Individual: {self.pop[0]}")
1531-
print(f"Accepted rate: {self.acceptance[self.NumGens]['accepted']} / {self.acceptance[self.NumGens]['generated']}\n")
1537+
log(f"Initial Population: Best Individual: {self.pop[0]}")
1538+
log(f"Acceptance rate: {self.acceptance[self.NumGens]['accepted']} / {self.acceptance[self.NumGens]['generated']}\n")
15321539
# Updating the info of the first individual (parent)
15331540
# to print at the end how well performed the method (cost function)
15341541
# Because How the population was initialized and because we are using pool.imap (ordered).
@@ -1606,7 +1613,7 @@ def __call__(self, njobs: int = 1, runner: Optional[Runner] = None):
16061613
individual.idx = i + NumbOfSawIndividuals
16071614
# The problem here is that we are not being general for other possible Cost functions.
16081615
args_list.append((individual, kwargs_copy))
1609-
print(f'Evaluating generation {self.NumGens} / {self.maxiter + number_of_previous_generations}:')
1616+
log(f'Evaluating generation {self.NumGens} / {self.maxiter + number_of_previous_generations}:')
16101617

16111618
# Calculating cost function in parallel
16121619
popc = runner.run(self.__costfunc__, args_list)
@@ -1650,24 +1657,24 @@ def __call__(self, njobs: int = 1, runner: Optional[Runner] = None):
16501657
compressed_pickle('cpt', self)
16511658

16521659
# Show Iteration Information
1653-
print(f"Generation {self.NumGens}: Best Individual: {self.pop[0]}.")
1654-
print(f"Accepted rate: {self.acceptance[self.NumGens]['accepted']} / {self.acceptance[self.NumGens]['generated']}\n")
1660+
log(f"Generation {self.NumGens}: Best Individual: {self.pop[0]}")
1661+
log(f"Acceptance rate: {self.acceptance[self.NumGens]['accepted']} / {self.acceptance[self.NumGens]['generated']}\n")
16551662

16561663
# Printing summary information
1657-
print(f"\n{50*'=+'}\n")
1658-
print(f"The simulation finished successfully after {self.NumGens} generations with"
1659-
f"a population of {self.popsize} individuals. "
1660-
f"A total number of {len(self.SawIndividuals)} Individuals were seen during the simulation.")
1661-
print(f"Initial Individual: {self.InitIndividual}")
1662-
print(f"Final Individual: {self.pop[0]}")
1663-
print(f"The cost function dropped in {self.InitIndividual - self.pop[0]} units.")
1664-
print(f"\n{50*'=+'}\n")
1664+
log(f"\t\t{20*'=+'}\n")
1665+
log(f"The simulation finished successfully after {self.NumGens} generations with"
1666+
f"a population of {self.popsize} individuals. "
1667+
f"A total number of {len(self.SawIndividuals)} Individuals were seen during the simulation.")
1668+
log(f"Initial Individual: {self.InitIndividual}")
1669+
log(f"Final Individual: {self.pop[0]}")
1670+
log(f"The cost function dropped in {self.InitIndividual - self.pop[0]} units.")
1671+
log(f"\t\t{20*'=+'}\n")
16651672

16661673
# Tar errors
16671674
tar_errors('error')
16681675

16691676
# Printing how long was the simulation
1670-
print(f"Total time ({self.maxiter} generations): {time.time() - ts:>5.2f} (s).\n"
1677+
log(f"Total time ({self.maxiter} generations): {time.time() - ts:>5.2f} (s).\n"
16711678
f"Finished at {datetime.datetime.now().strftime('%c')}.\n")
16721679

16731680
def __costfunc__(self, args_list):
@@ -1712,7 +1719,7 @@ def mutate(self, individual: Individual):
17121719
else:
17131720
_, mol = random.choice(mutants) # nosec
17141721
except Exception:
1715-
print(f'Note: The mutation on {individual} did not work, it will be returned the same individual')
1722+
log(f'The mutation on {individual} did not work, it will be returned the same individual', LogLevel.WARNING)
17161723
mol = individual.mol
17171724
if self.AddHs:
17181725
mol = Chem.AddHs(mol)

0 commit comments

Comments
 (0)