From 8320238b13ab970b2819962542a0d521e43479ad Mon Sep 17 00:00:00 2001 From: brockdyer03 Date: Sat, 5 Sep 2026 00:20:40 -0400 Subject: [PATCH 1/3] Nexus: Initial rework of `NexusCore.log()` to `NexusCore.nxs_print()` --- nexus/nexus/__init__.py | 10 +-- nexus/nexus/developer.py | 2 +- .../01_diamond_scf/diamond_lda.py | 6 +- nexus/nexus/machines.py | 38 +++++------ nexus/nexus/nexus_base.py | 65 ++++++++++--------- nexus/nexus/project_manager.py | 54 ++++++++------- nexus/nexus/pwscf_postprocessors.py | 8 +-- nexus/nexus/pyscf_sim.py | 2 +- nexus/nexus/qmcpack.py | 8 +-- nexus/nexus/qmcpack_analyzer.py | 4 +- nexus/nexus/qmcpack_analyzer_base.py | 12 ++-- nexus/nexus/qmcpack_method_analyzers.py | 4 +- nexus/nexus/simulation.py | 20 +++--- nexus/nexus/tests/test_nexus_base.py | 10 ++- nexus/nexus/tests/test_project_manager.py | 23 +++---- 15 files changed, 133 insertions(+), 133 deletions(-) diff --git a/nexus/nexus/__init__.py b/nexus/nexus/__init__.py index 4099638c6a..fc563aa50c 100644 --- a/nexus/nexus/__init__.py +++ b/nexus/nexus/__init__.py @@ -218,7 +218,7 @@ def __call__(self,**kwargs): write_splash() # print version information - self.log("Checking current machine for Nexus dependencies...\n") + self.nxs_print("Checking current machine for Nexus dependencies...\n") pkg_sort = { "numpy": 0, "scipy": 1, @@ -293,9 +293,9 @@ def __call__(self,**kwargs): version_text += "\n" - self.log(version_text) + self.nxs_print(version_text) - self.log('Applying user settings') + self.nxs_print('Applying user settings') # assign simple variables for name in Settings.core_assign_vars: @@ -505,9 +505,9 @@ def process_machine_settings(self,mset): if 'machine' in mset: machine_name = mset.machine if machine_name in {"ws", "workstation"}: - self.log("Automatically detecting physical CPU cores for workstation...", n=1) + self.nxs_print("Automatically detecting physical CPU cores for workstation...", n=1) n_cores = get_cpu_cores() - self.log(f"Using {n_cores} core workstation", n=1) + self.nxs_print(f"Using {n_cores} core workstation", n=1) machine_name = f"ws{n_cores}" if not Machine.exists(machine_name): diff --git a/nexus/nexus/developer.py b/nexus/nexus/developer.py index 384cb802fb..9e75777306 100644 --- a/nexus/nexus/developer.py +++ b/nexus/nexus/developer.py @@ -64,7 +64,7 @@ def _logfile(self): return generic_settings.devlog - def log(self,*a,**kw): + def nxs_print(self,*a,**kw): kw.setdefault('logfile',self._logfile) log(*a,**kw) diff --git a/nexus/nexus/examples/quantum_espresso/01_diamond_scf/diamond_lda.py b/nexus/nexus/examples/quantum_espresso/01_diamond_scf/diamond_lda.py index ea006f65c6..ef83c3e95f 100755 --- a/nexus/nexus/examples/quantum_espresso/01_diamond_scf/diamond_lda.py +++ b/nexus/nexus/examples/quantum_espresso/01_diamond_scf/diamond_lda.py @@ -7,8 +7,8 @@ settings( pseudo_dir = '../pseudopotentials', results = '', - sleep = 3, - machine = 'ws16', + sleep = 0.5, + machine = 'ws8', ) system = generate_physical_system( @@ -26,7 +26,7 @@ scf = generate_pwscf( identifier = 'scf', path = 'diamond/scf', - job = job(cores=16,app='pw.x'), + job = job(cores=6,app='pw.x'), input_type = 'generic', calculation = 'scf', input_dft = 'lda', diff --git a/nexus/nexus/machines.py b/nexus/nexus/machines.py index c1cebba0ee..896861c622 100644 --- a/nexus/nexus/machines.py +++ b/nexus/nexus/machines.py @@ -1267,43 +1267,43 @@ def process_job_options(self,job): def write_job_states(self,title=''): - self.log(title,n=2) + self.nxs_print(title,n=2) n=3 - self.log(f'{self.__class__.__name__} {self.name} {id(self)} job states',n=n ) - self.log('processes',n=n+1) + self.nxs_print(f'{self.__class__.__name__} {self.name} {id(self)} job states',n=n ) + self.nxs_print('processes',n=n+1) for process in self.processes: job = process.job - self.log(f'{job.internal_id:>4} {job.name:>10} {job.simid:>4} {job.directory}',n=n+2) + self.nxs_print(f'{job.internal_id:>4} {job.name:>10} {job.simid:>4} {job.directory}',n=n+2) #end for - self.log('jobs',n=n+1) + self.nxs_print('jobs',n=n+1) jobids = list(self.jobs.keys()) jobids.sort() for jobid in jobids: job = self.jobs[jobid] - self.log(f'{job.internal_id:>4} {job.name:>10} {job.simid:>4} {job.directory}',n=n+2) + self.nxs_print(f'{job.internal_id:>4} {job.name:>10} {job.simid:>4} {job.directory}',n=n+2) #end for - self.log('waiting',n=n+1) + self.nxs_print('waiting',n=n+1) jobids = list(self.waiting) jobids.sort() for jobid in jobids: job = self.jobs[jobid] - self.log(f'{job.internal_id:>4} {job.name:>10} {job.simid:>4} {job.directory}',n=n+2) + self.nxs_print(f'{job.internal_id:>4} {job.name:>10} {job.simid:>4} {job.directory}',n=n+2) #end for - self.log('running',n=n+1) + self.nxs_print('running',n=n+1) jobids = list(self.running) jobids.sort() for jobid in jobids: job = self.jobs[jobid] - self.log(f'{job.internal_id:>4} {job.name:>10} {job.simid:>4} {job.directory}',n=n+2) + self.nxs_print(f'{job.internal_id:>4} {job.name:>10} {job.simid:>4} {job.directory}',n=n+2) #end for - self.log('finished',n=n+1) + self.nxs_print('finished',n=n+1) jobids = list(self.finished) jobids.sort() for jobid in jobids: job = self.jobs[jobid] - self.log(f'{job.internal_id:>4} {job.name:>10} {job.simid:>4} {job.directory}',n=n+2) + self.nxs_print(f'{job.internal_id:>4} {job.name:>10} {job.simid:>4} {job.directory}',n=n+2) #end for - self.log('end job states',n=1) + self.nxs_print('end job states',n=1) #end def write_job_states @@ -1444,11 +1444,11 @@ def submit_job(self,job): process = obj() process.job = job if nexus_core.generate_only: - self.log(pad+'Would have executed: '+command) + self.nxs_print(pad+'Would have executed: '+command) job.system_id = job.internal_id else: if nexus_core.monitor: - self.log(pad+'Executing: '+command) + self.nxs_print(pad+'Executing: '+command) job.out = open(job.outfile,'w') job.err = open(job.errfile,'w') p = Popen(command,env=job.env,stdout=job.out,stderr=job.err,shell=True) @@ -1456,7 +1456,7 @@ def submit_job(self,job): job.system_id = p.pid else: command+=' >'+job.outfile+' 2>'+job.errfile+'&' - self.log(pad+'Executing: '+command) + self.nxs_print(pad+'Executing: '+command) os.system(command) job.system_id = job.internal_id #end if @@ -2139,13 +2139,13 @@ def submit_job(self,job): #end if command = self.sub_command(job) if nexus_core.generate_only: - self.log(pad+'Would have executed: '+command) + self.nxs_print(pad+'Would have executed: '+command) job.status = job.states.running process = obj() process.job = job self.processes[job.internal_id] = process else: - self.log(pad+'Executing: '+command) + self.nxs_print(pad+'Executing: '+command) job.status = job.states.running process = obj() process.job = job @@ -2159,7 +2159,7 @@ def submit_job(self,job): ) raise RuntimeError(msg) else: - self.log(pad+f' pid: {pid}') + self.nxs_print(pad+f' pid: {pid}') #end if #pid = 'fakepid_'+str(job.internal_id) job.system_id = pid diff --git a/nexus/nexus/nexus_base.py b/nexus/nexus/nexus_base.py index fc371a94eb..973ddf8137 100644 --- a/nexus/nexus/nexus_base.py +++ b/nexus/nexus/nexus_base.py @@ -26,16 +26,19 @@ import os -from os import PathLike -from copy import deepcopy import pickle -from pickle import UnpicklingError +import sys +import textwrap +from copy import deepcopy +from os import PathLike from pathlib import Path -from .utilities import path_string -from .nexus_version import nexus_version -from .memory import resident -from .developer import DevBase, obj, log +from pickle import UnpicklingError +from typing import Literal +from .developer import DevBase, log, obj +from .memory import resident +from .nexus_version import nexus_version +from .utilities import path_string # Nexus namespaces # nexus_core: to be used by NexusCore classes only @@ -173,43 +176,41 @@ def mem_usage(self): return int(resident()/1e6) #end def mem_usage - def log(self,*texts,**kwargs): - """Write output to log file. + def nxs_print(self, *texts: str, n: int = 0, progress: bool = False): + """Write text to standard output. Parameters ---------- *texts - Strings that will be joined by newlines + Strings that will be joined by newline characters. n : int, kwargs - Spaces to indent + Spaces to indent by. progress : bool, kwargs If ``True`` and output is to a terminal, overwrite and update the last line, rather than scrolling. """ if nexus_core.verbose: - if len(kwargs)>0: - n = kwargs['n'] + text = ' '.join(str(t) for t in texts) + output_text = textwrap.indent(text, n * nexus_core.indent) + if nexus_core.progress_tty and progress and sys.stdout.isatty(): + # Line up + Line clear ANSI sequence + sys.stdout.write('\033[1A'+'\x1b[2K') + sys.stdout.write(output_text+"\n") + sys.stdout.flush() else: - n=0 - #end if - is_progress = kwargs.get('progress',False) - text='' - for t in texts: - text+=str(t)+' ' - #end for - pad = n*nexus_core.indent - output_text = pad+text.replace('\n','\n'+pad) - if nexus_core.progress_tty and is_progress and self._logfile.isatty(): - # spaces to ensure previous line is overwritten. Need better solution. - self._logfile.write(output_text+' \r') - self._logfile.flush() - else: - self._logfile.write(output_text+'\n') - #end if + sys.stdout.write(output_text+'\n') + sys.stdout.flush() + NexusCore.wrote_something = True #end def log - def enter(self, directory: PathLike, *, changedir: bool = True, msg: str = ''): + def enter( + self, + directory: PathLike, + *, + changedir: bool = True, + msg: str = '', + ) -> Literal[' ']: """Have Nexus enter a directory and change its current working directory. Parameters @@ -226,10 +227,10 @@ def enter(self, directory: PathLike, *, changedir: bool = True, msg: str = ''): NexusCore.working_directory = os.getcwd() directory = path_string(directory) - self.log(' Entering ' + directory, msg) + self.nxs_print('\nEntering ' + directory, msg, n=2) if changedir: os.chdir(directory) - #end if + pad = ' ' return pad #end def enter diff --git a/nexus/nexus/project_manager.py b/nexus/nexus/project_manager.py index 255d558081..f7a7a28dc1 100644 --- a/nexus/nexus/project_manager.py +++ b/nexus/nexus/project_manager.py @@ -96,7 +96,7 @@ def add_cascade(self,cascade): def run_project(self,*,status=False,status_only=False): - self.log('\nProject starting',n=0) + self.nxs_print('\nProject starting',n=0) self.init_cascades() status_only = status_only or nexus_core.status_only status = status or status_only or nexus_core.status!=nexus_core.status_modes.none @@ -106,16 +106,19 @@ def run_project(self,*,status=False,status_only=False): return #end if #end if - self.log('\nstarting runs:\n'+30*'~',n=1) + self.nxs_print('\nstarting runs:\n'+30*'~',n=1) if nexus_core.dependent_modes <= nexus_core.stages_set: if nexus_core.monitor: start_time = time.time() ipoll = 0 while len(self.progressing_cascades)>0: elapsed_time = time.time() - start_time - self.log('elapsed time %.1f s'%elapsed_time, - ' memory %3.2f MB'%(memory.resident(children=True)/1e6), - n=1,progress=True) + mem_usage = (memory.resident(children=True)/1e6) + self.nxs_print( + f'elapsed time {elapsed_time:.1f} s memory {mem_usage:3.2f} MB', + n=1, + progress=True, + ) NexusCore.wrote_something = False ipoll+=1 self.machine.query_queue() @@ -124,7 +127,10 @@ def run_project(self,*,status=False,status_only=False): self.update_process_ids() time.sleep(nexus_core.sleep) if NexusCore.wrote_something: - self.log() + if nexus_core.progress_tty: + self.nxs_print("\n") + else: + self.nxs_print() #end if #end while elif len(self.progressing_cascades)>0: @@ -136,7 +142,7 @@ def run_project(self,*,status=False,status_only=False): else: self.progress_cascades() #end if - self.log('Project finished\n') + self.nxs_print('Project finished\n') #end def run_project @@ -144,14 +150,14 @@ def init_cascades(self): self.screen_fake_sims() self.resolve_file_collisions() self.propagate_blockages() - self.log('loading cascade images',n=1) + self.nxs_print('loading cascade images',n=1) if nexus_core.load_images: self.load_cascades() else: - self.log('cascades',n=1) + self.nxs_print('cascades',n=1) #end if for c in self.progressing_cascades.values(): - self.log('cascade',c.simid,'checking in',n=2) + self.nxs_print('cascade',c.simid,'checking in',n=2) #end for self.check_dependencies() #end def init_cascades @@ -180,7 +186,7 @@ def collect_fake(sim,fake): def resolve_file_collisions(self): - self.log('checking for file collisions',n=1) + self.nxs_print('checking for file collisions',n=1) entry_order = obj() def set_entry_order(sim,entry_order): locdir = sim.locdir @@ -216,7 +222,7 @@ def set_entry_order(sim,entry_order): for sim in sims: msg +=str(sim.identifier)+' '+str(sim.simid)+',' #end for - self.log(msg[:-1],n=2) + self.nxs_print(msg[:-1],n=2) collpath = path #end if #end for @@ -261,12 +267,12 @@ def load_cascades(self): def check_dependencies(self): - self.log('checking cascade dependencies',n=1) + self.nxs_print('checking cascade dependencies',n=1) result = obj() result.dependencies_satisfied = True self.traverse_cascades(Simulation.check_dependencies,result) if result.dependencies_satisfied: - self.log('all simulation dependencies satisfied',n=2) + self.nxs_print('all simulation dependencies satisfied',n=2) else: msg = 'some simulation dependecies are not satisfied' raise RuntimeError(msg) @@ -288,8 +294,8 @@ def traverse_cascades(self,operation=trivial,*args,**kwargs): def write_simulation_status(self): status = nexus_core.status status_modes = nexus_core.status_modes - self.log('\ncascade status',n=1) - self.log('setup, sent_files, submitted, finished, got_output, analyzed, failed',n=2) + self.nxs_print('\ncascade status',n=1) + self.nxs_print('setup, sent_files, submitted, finished, got_output, analyzed, failed',n=2) all_sids = set() for sim in self.simulations.values(): add = False @@ -325,16 +331,16 @@ def write_simulation_status(self): #end for sids = all_sids-sids if len(sids)>0: - self.log('==== sims missed, part of bundles? ====',n=2) + self.nxs_print('==== sims missed, part of bundles? ====',n=2) for isim in sorted(sids): sim = self.simulations[isim] self.status_line(sim) #end for #end if if len(all_sids)==0: - self.log('(No simulations present)',n=2) + self.nxs_print('(No simulations present)',n=2) #end if - self.log('setup, sent_files, submitted, finished, got_output, analyzed, failed',n=2) + self.nxs_print('setup, sent_files, submitted, finished, got_output, analyzed, failed',n=2) #end def write_simulation_status @@ -356,7 +362,7 @@ def status_line(self,sim,extra=''): #end if result = color_status_result(result,self._logfile) sline = f'{status} {result:<7} {pid:<8} {sim.identifier:<6} {sim.locdir}' - self.log(sline,extra,n=2) + self.nxs_print(sline,extra,n=2) #end def status_line @@ -396,12 +402,12 @@ def write_sim_dependencies(self,idkey=None): for simid in sorted(self.simulations.keys()): sim = self.simulations[simid] if idkey is None or sim.identifier==idkey: - self.log(f'\n{sim.identifier} {simid} {sim.locdir}') + self.nxs_print(f'\n{sim.identifier} {simid} {sim.locdir}') for did in sorted(sim.dependencies.keys()): dep = sim.dependencies[did] dsim = dep.sim names = dep.result_names - self.log(f' {dsim.identifier} {dsim.simid} {names} {dsim.locdir}') + self.nxs_print(f' {dsim.identifier} {dsim.simid} {names} {dsim.locdir}') #end for #end if #end for @@ -410,12 +416,12 @@ def write_sim_dependencies(self,idkey=None): # test needed def write_cascade_dependents(self): - self.log('cascade dependents',n=1) + self.nxs_print('cascade dependents',n=1) for cascade in self.cascades: cascade.reset_wait_ids() #end for for cascade in self.cascades: - self.log(cascade.__class__.__name__+' '+str(cascade.simid),n=2) + self.nxs_print(cascade.__class__.__name__+' '+str(cascade.simid),n=2) cascade.write_dependents(n=2) #end for return diff --git a/nexus/nexus/pwscf_postprocessors.py b/nexus/nexus/pwscf_postprocessors.py index 4f70422173..319b46624a 100644 --- a/nexus/nexus/pwscf_postprocessors.py +++ b/nexus/nexus/pwscf_postprocessors.py @@ -628,11 +628,11 @@ def analyze(self): def open_log(self): logfile = os.path.join(self.info.path,self.info.outfile) - self.log = TextFile(logfile) + self.nxs_print = TextFile(logfile) #end def open_log def read_states(self): - log = self.log + log = self.nxs_print log.seek('state #') nstates = 0 elem_ind = set() @@ -654,7 +654,7 @@ def read_states(self): #end def read_states def read_lowdin(self): - log = self.log + log = self.nxs_print log.seek('Lowdin Charges') lowdin = obj() has_ud = False @@ -804,7 +804,7 @@ def write_lowdin(self,filepath=None,sum=None,tot=None,pol=None,up=None,down=None def close_log(self): if 'log' in self: - del self.log + del self.nxs_print #end if #end def close_log diff --git a/nexus/nexus/pyscf_sim.py b/nexus/nexus/pyscf_sim.py index ac211b0c5e..1e786d9fb9 100644 --- a/nexus/nexus/pyscf_sim.py +++ b/nexus/nexus/pyscf_sim.py @@ -110,7 +110,7 @@ def check_sim_status(self): if self.failed: error_txt = textwrap.indent(errors, " ", lambda _: True) - self.log( + self.nxs_print( "\n" "PySCF run failed!\n" "Error File Contents:\n\n" diff --git a/nexus/nexus/qmcpack.py b/nexus/nexus/qmcpack.py index d126bf5db1..d39c02e201 100644 --- a/nexus/nexus/qmcpack.py +++ b/nexus/nexus/qmcpack.py @@ -610,7 +610,7 @@ def write_gcta_report(self, locdir, fermi_level, scf_magnet = None): gcta_file.write('\n') #end for #end with - self.log(f' See the GCTA occupation report at: {filepath}') + self.nxs_print(f' See the GCTA occupation report at: {filepath}') #end def write_gcta_report #end class GCTA @@ -1060,7 +1060,7 @@ def incorporate_result(self,result_name,result,sim): gcta_obj.check_implementation(gcta_possible, gcta_dependency) - gcta_obj.log(' Reading the eigenvalue and k-point data for GCTA. This might take a while.') + gcta_obj.nxs_print(' Reading the eigenvalue and k-point data for GCTA. This might take a while.') if isinstance(gcta_dependency,Pw2qmcpack) or isinstance(gcta_dependency,Convertpw4qmc): gcta_obj.read_eshdf_data(h5file) else: @@ -1401,8 +1401,8 @@ def check_sim_status(self): if ran_to_end and not files_exist: self.warn('run finished successfully, but output files do not exist') - self.log(outfiles) - self.log(os.listdir(self.locdir)) + self.nxs_print(outfiles) + self.nxs_print(os.listdir(self.locdir)) #end if #end if diff --git a/nexus/nexus/qmcpack_analyzer.py b/nexus/nexus/qmcpack_analyzer.py index 264dea6039..8ec5021478 100644 --- a/nexus/nexus/qmcpack_analyzer.py +++ b/nexus/nexus/qmcpack_analyzer.py @@ -701,7 +701,7 @@ def check_traces(self,*,verbose=False,pad=None,header=None): #end if if 'qmc' in self: if verbose: - self.log(pad+header) + self.nxs_print(pad+header) pad += ' ' #end if for method in self.qmc.values(): @@ -709,7 +709,7 @@ def check_traces(self,*,verbose=False,pad=None,header=None): #end for else: if verbose: - self.log(pad+'\nNo traces to check') + self.nxs_print(pad+'\nNo traces to check') #end if return None #end if diff --git a/nexus/nexus/qmcpack_analyzer_base.py b/nexus/nexus/qmcpack_analyzer_base.py index 07d1862afb..4d13c80675 100644 --- a/nexus/nexus/qmcpack_analyzer_base.py +++ b/nexus/nexus/qmcpack_analyzer_base.py @@ -225,17 +225,17 @@ def write(self,pad=''): #end if valid = self._valid if valid: - self.log(pad+self._label+' is valid') + self.nxs_print(pad+self._label+' is valid') else: - self.log(pad+self._label+' is invalid') + self.nxs_print(pad+self._label+' is invalid') for name,value in self.items(): if not (isinstance(name,str) and name.startswith('_')): if value in self._exclusions: - self.log(pad2+name+' could not be checked') + self.nxs_print(pad2+name+' could not be checked') elif value: - self.log(pad2+name+' is valid') + self.nxs_print(pad2+name+' is valid') else: - self.log(pad2+name+' is invalid') + self.nxs_print(pad2+name+' is invalid') #end if #end if #end for @@ -381,7 +381,7 @@ def subindent(self): def vlog(self,msg,n=0): if QAanalyzer.verbose_vlog: - self.log(msg,n=self.info.nindent+n) + self.nxs_print(msg,n=self.info.nindent+n) #end if #end def vlog diff --git a/nexus/nexus/qmcpack_method_analyzers.py b/nexus/nexus/qmcpack_method_analyzers.py index 43e162d843..e3110942ca 100644 --- a/nexus/nexus/qmcpack_method_analyzers.py +++ b/nexus/nexus/qmcpack_method_analyzers.py @@ -261,7 +261,7 @@ def check_traces(self,pad=None): if 'traces' in self: check = {None:True,False:False,True:True} if verbose: - self.log(pad+'Checking traces in '+desc) + self.nxs_print(pad+'Checking traces in '+desc) #end if scalars = None scalars_hdf = None @@ -294,7 +294,7 @@ def check_traces(self,pad=None): return valid else: if verbose: - self.log(pad+'No traces in '+desc) + self.nxs_print(pad+'No traces in '+desc) #end if return None #end if diff --git a/nexus/nexus/simulation.py b/nexus/nexus/simulation.py index 9cf51d3845..081f698662 100644 --- a/nexus/nexus/simulation.py +++ b/nexus/nexus/simulation.py @@ -1069,7 +1069,7 @@ def idstr(self): def write_inputs(self,*,save_image=True): self.pre_write_inputs(save_image) self.enter(self.locdir,changedir=False,msg=self.simid) - self.log('writing input files'+self.idstr(),n=3) + self.nxs_print('writing input files'+self.idstr(),n=3) self.write_prep() if self.infile is not None: infile = os.path.join(self.locdir,self.infile) @@ -1106,7 +1106,7 @@ def send_files(self,*,enter=True): if enter: self.enter(self.locdir,changedir=False,msg=self.simid) #end if - self.log('sending required files'+self.idstr(),n=3) + self.nxs_print('sending required files'+self.idstr(),n=3) if not os.path.exists(self.remdir): os.makedirs(self.remdir) #end if @@ -1158,7 +1158,7 @@ def submit(self): self.block_dependents(block_self=True) return #end if - self.log('submitting job'+self.idstr(),n=3) + self.nxs_print('submitting job'+self.idstr(),n=3) if not self.skip_submit: if not self.job.local: self.job.submit() @@ -1251,7 +1251,7 @@ def get_output(self): #end if if self.finished: self.enter(self.locdir,changedir=False,msg=self.simid) - self.log('copying results'+self.idstr(),n=3) + self.nxs_print('copying results'+self.idstr(),n=3) if not nexus_core.generate_only: output_files = self.get_output_files() if self.infile is not None: @@ -1273,9 +1273,9 @@ def get_output(self): #end if #end for if len(files_missing)>0: - self.log('warning: the following files were missing',n=4) + self.nxs_print('warning: the following files were missing',n=4) for file in files_missing: - self.log(file,n=5) + self.nxs_print(file,n=5) #end for #end if #end if @@ -1292,7 +1292,7 @@ def analyze(self): #end if if self.finished: self.enter(self.locdir,changedir=False,msg=self.simid) - self.log('analyzing'+self.idstr(),n=3) + self.nxs_print('analyzing'+self.idstr(),n=3) if not nexus_core.generate_only: analyzer = self.analyzer_type(self) analyzer.analyze() @@ -1500,7 +1500,7 @@ def write_dependents(self,n=0,*,location=False,block_status=False): #end if #end if outs.append(list(self.dependency_ids)) - self.log(*outs,n=n) + self.nxs_print(*outs,n=n) n+=1 for sim in self.dependents.values(): sim.write_dependents(n=n,location=location,block_status=block_status) @@ -1530,9 +1530,9 @@ def execute(self,run_command=None): env = job.env #end if if nexus_core.generate_only: - self.log(pad+'Would have executed: '+command) + self.nxs_print(pad+'Would have executed: '+command) else: - self.log(pad+'Executing: '+command) + self.nxs_print(pad+'Executing: '+command) with open(self.outfile,'w') as fout, open(self.errfile,'w') as ferr: out,err = Popen(command,env=env,stdout=fout,stderr=ferr,shell=True,close_fds=True).communicate() #end if diff --git a/nexus/nexus/tests/test_nexus_base.py b/nexus/nexus/tests/test_nexus_base.py index ea821df9f0..358ae8d1d2 100644 --- a/nexus/nexus/tests/test_nexus_base.py +++ b/nexus/nexus/tests/test_nexus_base.py @@ -50,21 +50,19 @@ def test_write_splash(): #end def test_write_splash -@isolate_nexus_core -def test_enter_leave(tmp_path): +def test_enter_leave(tmp_path, capsys): import os from ..nexus_base import NexusCore cwd = os.getcwd() - log = generic_settings.devlog - nc = NexusCore() nc.enter(tmp_path) tcwd = os.getcwd() assert(tcwd==str(tmp_path)) - assert('Entering' in log.contents()) - assert(str(tmp_path) in log.contents()) + captured = capsys.readouterr() + assert('Entering' in captured.out) + assert(str(tmp_path) in captured.out) nc.leave() assert(os.getcwd()==cwd) diff --git a/nexus/nexus/tests/test_project_manager.py b/nexus/nexus/tests/test_project_manager.py index b953d79d38..0e1e03a9b0 100644 --- a/nexus/nexus/tests/test_project_manager.py +++ b/nexus/nexus/tests/test_project_manager.py @@ -1,3 +1,5 @@ +import sys + import pytest from . import NexusTestOrder pytestmark = pytest.mark.order(NexusTestOrder.PROJECT_MANAGER) @@ -274,8 +276,7 @@ def test_check_dependencies(): #end def test_check_dependencies -@isolate_nexus_core -def test_write_simulation_status(): +def test_write_simulation_status(capsys): from ..generic import generic_settings from ..nexus_base import nexus_core from ..simulation import Simulation @@ -283,8 +284,6 @@ def test_write_simulation_status(): from .test_simulation_module import get_test_workflow - log = generic_settings.devlog - sims = get_test_workflow(3) for id,sim in sims.items(): sim.identifier = 'test_sim_'+id @@ -296,9 +295,9 @@ def test_write_simulation_status(): status_modes = nexus_core.status_modes def status_log(): - log.reset() pm.write_simulation_status() - s = log.contents() + s = capsys.readouterr().out + return '\n'.join(line.rstrip() for line in s.splitlines()) #end def status_log @@ -347,24 +346,22 @@ def status_log(): sim.sent_files = True sim.submitted = True - log.reset() pm.status_line(sim) - assert(log.contents().strip()=='111000 ------ test_sim_s11 ./runs/') + assert(capsys.readouterr().out.strip()=='111000 ------ test_sim_s11 ./runs/') sim.finished = True sim.got_output = True sim.analyzed = True - log.reset() pm.status_line(sim) - assert(log.contents().strip()=='111111 SUCCESS ------ test_sim_s11 ./runs/') + assert(capsys.readouterr().out.strip()=='111111 SUCCESS ------ test_sim_s11 ./runs/') sim.failed = True - log.reset() pm.status_line(sim) - assert(log.contents().strip()=='111111 FAILURE ------ test_sim_s11 ./runs/') + assert(capsys.readouterr().out.strip()=='111111 FAILURE ------ test_sim_s11 ./runs/') + nexus_core.status = 0 Simulation.clear_all_sims() #end def test_write_simulation_status @@ -425,8 +422,6 @@ def test_run_project(tmp_path): nexus_core.sleep = 0.1 - log = generic_settings.devlog - flags = ['setup','sent_files','submitted','finished','got_output','analyzed'] def finished(s): From fc82dbb374ce4dd72cdbc6d5fb666d6231ec351e Mon Sep 17 00:00:00 2001 From: brockdyer03 Date: Sat, 5 Sep 2026 11:20:22 -0400 Subject: [PATCH 2/3] Nexus: Rework all `log()` to `nxs_print()` --- nexus/nexus/__init__.py | 2 +- nexus/nexus/basisset.py | 10 +++++----- nexus/nexus/bin/qdens | 10 +++++----- nexus/nexus/bin/qdens-radial | 24 ++++++++++++------------ nexus/nexus/bin/qmc-fit | 26 +++++++++++++------------- nexus/nexus/developer.py | 4 ++-- nexus/nexus/generic.py | 6 +++--- nexus/nexus/nexus_base.py | 4 ++-- nexus/nexus/observables.py | 4 ++-- nexus/nexus/pwscf_input.py | 4 ++-- nexus/nexus/pwscf_postprocessors.py | 8 ++++---- nexus/nexus/qmcpack_input.py | 4 ++-- nexus/nexus/quantum_package_input.py | 16 ++++++++-------- nexus/nexus/tests/test_generic.py | 14 +++++++------- 14 files changed, 68 insertions(+), 68 deletions(-) diff --git a/nexus/nexus/__init__.py b/nexus/nexus/__init__.py index fc563aa50c..5fe32a6fe6 100644 --- a/nexus/nexus/__init__.py +++ b/nexus/nexus/__init__.py @@ -30,7 +30,7 @@ from .nexus_version import nexus_version from .generic import generic_settings -from .developer import obj, log, NexusError +from .developer import obj, nxs_print, NexusError from .debug import ci from .utilities import path_string diff --git a/nexus/nexus/basisset.py b/nexus/nexus/basisset.py index b5aa9eaad6..97f892cf16 100644 --- a/nexus/nexus/basisset.py +++ b/nexus/nexus/basisset.py @@ -9,7 +9,7 @@ from types import MappingProxyType import numpy as np from .periodic_table import Elements -from .developer import DevBase, obj, log, NexusError +from .developer import DevBase, obj, nxs_print, NexusError from .fileio import TextFile from .utilities import path_string, to_str @@ -64,11 +64,11 @@ def readbs(self,*bsfiles): bsfiles = bsfiles[0] #end if bss = [] - log('') - log(' Basissets') + nxs_print('') + nxs_print(' Basissets') for filepath in bsfiles: filepath_str = str(filepath) - log(' reading basis: '+filepath_str) + nxs_print(' reading basis: '+filepath_str) ext = filepath_str.split('.')[-1].lower() if ext=='gms_bas' or ext=='bas': bs = gamessBasisFile(filepath_str) @@ -77,7 +77,7 @@ def readbs(self,*bsfiles): #end if bss.append(bs) #end for - log('') + nxs_print('') self.addbs(bss) #end def readbs diff --git a/nexus/nexus/bin/qdens b/nexus/nexus/bin/qdens index 746432843e..30f15339be 100755 --- a/nexus/nexus/bin/qdens +++ b/nexus/nexus/bin/qdens @@ -200,7 +200,7 @@ class QDBase(DevBase): def vlog(self,*args,**kwargs): if self.verbose: - DevBase.log(self,*args,**kwargs) + DevBase.nxs_print(self,*args,**kwargs) #end if #end def vlog @@ -210,7 +210,7 @@ class QDBase(DevBase): #end def vmlog def help(self): - self.log('\n'+self.parser.format_help().strip()+'\n') + self.nxs_print('\n'+self.parser.format_help().strip()+'\n') #end def help # options accessor functions @@ -1213,8 +1213,8 @@ class QMCDensityProcessor(QDBase): self.vlog(f'\n{self.name} initializing') if self.verbose: - self.log('\noptions provided:') - self.log(str(self.options)) + self.nxs_print('\noptions provided:') + self.nxs_print(str(self.options)) #end if @@ -1496,7 +1496,7 @@ class QMCDensityProcessor(QDBase): #end if if opt.verbose and opt.input_metadata is not None: - self.log(f'input density metadata found for series: {sorted(opt.input_metadata.series.keys())}') + self.nxs_print(f'input density metadata found for series: {sorted(opt.input_metadata.series.keys())}') #end if for file in files_in: diff --git a/nexus/nexus/bin/qdens-radial b/nexus/nexus/bin/qdens-radial index 5130d6d5f6..4f151dbef8 100755 --- a/nexus/nexus/bin/qdens-radial +++ b/nexus/nexus/bin/qdens-radial @@ -97,7 +97,7 @@ class QDRBase(DevBase): def vlog(self,*args,**kwargs): if self.verbose: - DevBase.log(self,*args,**kwargs) + DevBase.nxs_print(self,*args,**kwargs) #end if #end def vlog @@ -107,7 +107,7 @@ class QDRBase(DevBase): #end def vmlog def help(self): - self.log('\n'+self.parser.format_help().strip()+'\n') + self.nxs_print('\n'+self.parser.format_help().strip()+'\n') #end def help #end class QDBase @@ -305,7 +305,7 @@ class QMCDensityRadialProcessor(QDRBase): # if VMC file is given, perform extrapolation before # instantiating ChargeDensity object if opt.vmc_file is not None: - self.log('Extrapolating from VMC and DMC densities...') + self.nxs_print('Extrapolating from VMC and DMC densities...') vmc_xsf_data = XsfFile(filepath=opt.vmc_file) dmc_xsf_data = XsfFile(filepath=file) # Initialize extrapolated XsfFile object @@ -319,14 +319,14 @@ class QMCDensityRadialProcessor(QDRBase): dmc_xsf_data = XsfFile(filepath=file) file = deepcopy(dmc_xsf_data) if opt.write_extrap: - self.log('\nWARNING: You provided --write, however, extrapolated density will not be written since a VMC file was not provided (via --vmc=VMC_FILE).\n') + self.nxs_print('\nWARNING: You provided --write, however, extrapolated density will not be written since a VMC file was not provided (via --vmc=VMC_FILE).\n') #end if #end if show_error=False # If error files are given, then user wants resampled error bars if opt.dmc_err_file is not None: - self.log('Resampling to obtain error bar (NOTE: This can be slow)...') + self.nxs_print('Resampling to obtain error bar (NOTE: This can be slow)...') if opt.vmc_file is not None and opt.vmc_err_file is not None: # Data without errors vmc_xsf_data = XsfFile(filepath=opt.vmc_file) @@ -381,9 +381,9 @@ class QMCDensityRadialProcessor(QDRBase): #end if nsamples = opt.nsamples - self.log(f'Will compute {nsamples} samples...') + self.nxs_print(f'Will compute {nsamples} samples...') for i in range(nsamples): - self.log(f'sample: {i}') + self.nxs_print(f'sample: {i}') resample_xsf_data = deepcopy(file) for d1_idx, d1 in enumerate(file.data[3]['density']['density'].values): for d2_idx, d2 in enumerate(d1): @@ -500,7 +500,7 @@ class QMCDensityRadialProcessor(QDRBase): cd.set_attribute('density_units','A') # xsf format uses Angstrom units which makes the QMCPACK density units (e/Angstrom^3) after volume normalization #end if - self.log('\nNorm:',cd.norm()) + self.nxs_print('\nNorm:',cd.norm()) if opt.plot: if opt.species is None: @@ -534,9 +534,9 @@ class QMCDensityRadialProcessor(QDRBase): r = opt.radii[0] #end if if not show_error: - self.log(f'{cstr} Value of {at} Species at Cutoff {r} is: {rdens.tot[at].density[-1]:.8f}\n') + self.nxs_print(f'{cstr} Value of {at} Species at Cutoff {r} is: {rdens.tot[at].density[-1]:.8f}\n') else: - self.log(f'{cstr} Value of {at} Species at Cutoff {r} is: {rdens.tot[at].density[-1]:.8f} +/- {std_dict[at]:.8f}\n') + self.nxs_print(f'{cstr} Value of {at} Species at Cutoff {r} is: {rdens.tot[at].density[-1]:.8f} +/- {std_dict[at]:.8f}\n') #end if #end for else: @@ -549,9 +549,9 @@ class QMCDensityRadialProcessor(QDRBase): r = opt.radii[0] #end if if not show_error: - self.log(f'{cstr} Value of {at} Species at Cutoff {r} is: {rdens.tot[at].density[-1]:.8f}\n') + self.nxs_print(f'{cstr} Value of {at} Species at Cutoff {r} is: {rdens.tot[at].density[-1]:.8f}\n') else: - self.log(f'{cstr} Value of {at} Species at Cutoff {r} is: {rdens.tot[at].density[-1]:.8f} +/- {std_dict[at]:.8f}\n') + self.nxs_print(f'{cstr} Value of {at} Species at Cutoff {r} is: {rdens.tot[at].density[-1]:.8f} +/- {std_dict[at]:.8f}\n') #end if #end for #end if diff --git a/nexus/nexus/bin/qmc-fit b/nexus/nexus/bin/qmc-fit index 687fc8d04d..5fd0eac47f 100755 --- a/nexus/nexus/bin/qmc-fit +++ b/nexus/nexus/bin/qmc-fit @@ -85,7 +85,7 @@ def find_nexus_modules(): # Load Nexus modules find_nexus_modules() -from nexus.developer import obj,log +from nexus.developer import obj,nxs_print from nexus.numerics import jackknife,jackknife_aux from nexus.numerics import simstats,equilibration_length from nexus.numerics import curve_fit,least_squares @@ -424,7 +424,7 @@ def timestep_fit(args): scalar_files = sorted(opt.scalar_files) if len(scalar_files)==0: - log('\n'+parser.format_help().strip()+'\n') + nxs_print('\n'+parser.format_help().strip()+'\n') sys.exit() #end if @@ -472,11 +472,11 @@ def timestep_fit(args): pvals.append('({0} +/- {1})'.format(*stat_strings(pmean[n],perror[n]))) #end for - log('\nfit function : '+opt.fit_function) - log('fitted formula: '+func_info.format.format(*pvals)) + nxs_print('\nfit function : '+opt.fit_function) + nxs_print('fitted formula: '+func_info.format.format(*pvals)) for pname,pfunc in func_info.params: # noqa: B007 pm,pe = stat_strings(*auxres[pname]) - log(f'{pname:<14}: {pm} +/- {pe} Ha\n') + nxs_print(f'{pname:<14}: {pm} +/- {pe} Ha\n') #end for # plot the fit (if available) @@ -546,7 +546,7 @@ def hubbard_u_fit(args): #end if x = opt.exx else: - log("\n Please provide either EXX or Hubbard_U values") + nxs_print("\n Please provide either EXX or Hubbard_U values") #end if parse_list(opt,'equils',int,len1=True) @@ -562,8 +562,8 @@ def hubbard_u_fit(args): pvals.append('({0} +/- {1})'.format(*stat_strings(pmean[n],perror[n]))) #end for - log('\nfit function : '+opt.fit_function) - log('fitted formula: '+func_info.format.format(*pvals)) + nxs_print('\nfit function : '+opt.fit_function) + nxs_print('fitted formula: '+func_info.format.format(*pvals)) for pname,pfunc in func_info.params: # noqa: B007 for i in range(len(auxres[pname][0])): pm,pe = stat_strings(*np.array(auxres[pname])[:,i]) @@ -581,7 +581,7 @@ def hubbard_u_fit(args): else: param = pname #end if - log(f'root {i+1} {param:<14}: {pm} +/- {pe} {unit}') + nxs_print(f'root {i+1} {param:<14}: {pm} +/- {pe} {unit}') #end for # plot the fit (if available) @@ -657,13 +657,13 @@ def eos_fit(args): for n in range(len(pmean)): pvals.append('({0} +/- {1})'.format(*stat_strings(pmean[n],perror[n]))) #end for - log('\nfit function : '+opt.fit_function) - log('fitted formula: '+func_info.format.format(*pvals)) + nxs_print('\nfit function : '+opt.fit_function) + nxs_print('fitted formula: '+func_info.format.format(*pvals)) if 'params' in func_info.keys(): for pname,pfunc in func_info.params: # noqa: B007 pm,pe = stat_strings(*np.array(auxres[pname])) - log(f'{pname}: {pm} +/- {pe} ') + nxs_print(f'{pname}: {pm} +/- {pe} ') #end for # plot the fit (if available) @@ -751,7 +751,7 @@ if __name__=='__main__': fit_types = sorted(all_fit_functions.keys()) parser, args = parse_args() if len(args.scalar_files) == 0: - log('\n'+'Please provide scalar files'+'\n') + nxs_print('\n'+'Please provide scalar files'+'\n') parser.print_help() sys.exit() #end if diff --git a/nexus/nexus/developer.py b/nexus/nexus/developer.py index 9e75777306..4ceca3095a 100644 --- a/nexus/nexus/developer.py +++ b/nexus/nexus/developer.py @@ -27,7 +27,7 @@ from .generic import NexusError, FileFormatError, NotAnElementError # noqa: F401 -from .generic import error, log, warn, message # noqa: F401 +from .generic import error, nxs_print, warn, message # noqa: F401 import traceback @@ -66,7 +66,7 @@ def _logfile(self): def nxs_print(self,*a,**kw): kw.setdefault('logfile',self._logfile) - log(*a,**kw) + nxs_print(*a,**kw) def warn(self,msg,indent=' '): warn( diff --git a/nexus/nexus/generic.py b/nexus/nexus/generic.py index 126bb84f03..9ee234fbd6 100644 --- a/nexus/nexus/generic.py +++ b/nexus/nexus/generic.py @@ -116,7 +116,7 @@ def nocopy(value): #end def nocopy -def log( +def nxs_print( *items, indent: str | None = None, logfile: TextIO | None = None, @@ -156,8 +156,8 @@ def message(msg,header=None,post_header=' message:',indent=' ',logfile=None): else: header += post_header #end if - log('\n '+header,logfile=logfile) - log(msg.rstrip(),indent=indent,logfile=logfile) + nxs_print('\n '+header,logfile=logfile) + nxs_print(msg.rstrip(),indent=indent,logfile=logfile) #end def message diff --git a/nexus/nexus/nexus_base.py b/nexus/nexus/nexus_base.py index 973ddf8137..ed7974a1bd 100644 --- a/nexus/nexus/nexus_base.py +++ b/nexus/nexus/nexus_base.py @@ -35,7 +35,7 @@ from pickle import UnpicklingError from typing import Literal -from .developer import DevBase, log, obj +from .developer import DevBase, nxs_print, obj from .memory import resident from .nexus_version import nexus_version from .utilities import path_string @@ -160,7 +160,7 @@ def write_splash(): _____________________________________________________ '''.format(*nexus_version) - log(splash_text) + nxs_print(splash_text) write_splash.wrote_splash = True #end if #end def write_splash diff --git a/nexus/nexus/observables.py b/nexus/nexus/observables.py index 794ea0d7e8..7521063cb6 100644 --- a/nexus/nexus/observables.py +++ b/nexus/nexus/observables.py @@ -11,7 +11,7 @@ # Nexus imports from . import memory from .unit_converter import convert -from .developer import DevBase, obj, log, NexusError, FileFormatError +from .developer import DevBase, obj, nxs_print, NexusError, FileFormatError from .numerics import simstats from .grid_functions import grid_function, read_grid, StructuredGrid, grid as generate_grid from .grid_functions import SpheroidGrid,ParallelotopeGridFunction @@ -73,7 +73,7 @@ def __call__(self,msg,level='low',n=0,*,time=False,mem=False,width=75): self.tlast = tnow #end if #end if - log(msg,n=n+self.indent) + nxs_print(msg,n=n+self.indent) #end if #end def __init__ diff --git a/nexus/nexus/pwscf_input.py b/nexus/nexus/pwscf_input.py index 6fa327580c..13cdc16ae4 100644 --- a/nexus/nexus/pwscf_input.py +++ b/nexus/nexus/pwscf_input.py @@ -56,7 +56,7 @@ from numpy.linalg import inv from . import numpy_extensions as npe -from .developer import DevBase, log, obj, warn, NexusError, FileFormatError +from .developer import DevBase, nxs_print, obj, warn, NexusError, FileFormatError from .periodic_table import Elements from .physical_system import PhysicalSystem from .pseudoset import pp_elem_label, PseudoSet @@ -813,7 +813,7 @@ def check_section_classes(*,exit=True): #end if raise NexusError(msg) else: - log('pwscf input checks passed') + nxs_print('pwscf input checks passed') #end if if exit: sys.exit() diff --git a/nexus/nexus/pwscf_postprocessors.py b/nexus/nexus/pwscf_postprocessors.py index 319b46624a..4f70422173 100644 --- a/nexus/nexus/pwscf_postprocessors.py +++ b/nexus/nexus/pwscf_postprocessors.py @@ -628,11 +628,11 @@ def analyze(self): def open_log(self): logfile = os.path.join(self.info.path,self.info.outfile) - self.nxs_print = TextFile(logfile) + self.log = TextFile(logfile) #end def open_log def read_states(self): - log = self.nxs_print + log = self.log log.seek('state #') nstates = 0 elem_ind = set() @@ -654,7 +654,7 @@ def read_states(self): #end def read_states def read_lowdin(self): - log = self.nxs_print + log = self.log log.seek('Lowdin Charges') lowdin = obj() has_ud = False @@ -804,7 +804,7 @@ def write_lowdin(self,filepath=None,sum=None,tot=None,pol=None,up=None,down=None def close_log(self): if 'log' in self: - del self.nxs_print + del self.log #end if #end def close_log diff --git a/nexus/nexus/qmcpack_input.py b/nexus/nexus/qmcpack_input.py index fb7ba1d403..3d0cdcbef0 100644 --- a/nexus/nexus/qmcpack_input.py +++ b/nexus/nexus/qmcpack_input.py @@ -142,7 +142,7 @@ import numpy as np from .numpy_extensions import reshape_inplace from .xmlreader import XMLreader, XMLelement -from .developer import DevBase, dotdict, obj, log, warn, FileFormatError, NexusError, sorted_generic +from .developer import DevBase, dotdict, obj, nxs_print, warn, FileFormatError, NexusError, sorted_generic from .periodic_table import Elements from .structure import Structure, Jellium, get_kpath from .physical_system import PhysicalSystem @@ -444,7 +444,7 @@ def write(self,s): def log(self,*items,**kwargs): if 'logfile' not in kwargs and '_logfile' in self.__dict__: kwargs['logfile'] = self._logfile - log(*items,**kwargs) + nxs_print(*items,**kwargs) #end def log def warn(self,message,header=None): diff --git a/nexus/nexus/quantum_package_input.py b/nexus/nexus/quantum_package_input.py index 638100f371..ce904f0dec 100644 --- a/nexus/nexus/quantum_package_input.py +++ b/nexus/nexus/quantum_package_input.py @@ -18,7 +18,7 @@ import os from copy import deepcopy -from .developer import DevBase, obj, log, NexusError +from .developer import DevBase, obj, nxs_print, NexusError from .structure import Structure from .physical_system import PhysicalSystem from .simulation import SimulationInput @@ -233,7 +233,7 @@ def extract_input_specification(*ezfio_paths): if len(ezfio_paths)==1 and isinstance(ezfio_paths[0],(list,tuple)): ezfio_paths = ezfio_paths[0] #end if - log('\nextracting Quantum Package input specification from ezfio directories') + nxs_print('\nextracting Quantum Package input specification from ezfio directories') typedict = {bool:'bool',int:'int',float:'float',str:'str'} new_input_spec = obj() for vpath,vtype in input_specification.items(): @@ -256,7 +256,7 @@ def extract_input_specification(*ezfio_paths): ) raise FileNotFoundError(msg) #end if - log(f' extracting from: {epath}') + nxs_print(f' extracting from: {epath}') for path,dirs,files in os.walk(epath): # noqa: B007 for file in files: if 'save' not in path and 'work' not in path: @@ -272,22 +272,22 @@ def extract_input_specification(*ezfio_paths): #end for #end for #end for - log(' extraction complete') + nxs_print(' extraction complete') old_vpaths = set(input_specification.keys()) new_vpaths = set(new_input_spec.keys()) if new_vpaths==old_vpaths: - log('\ninput specification in quantum_package_input.py needs no changes\n') + nxs_print('\ninput specification in quantum_package_input.py needs no changes\n') else: - log('\nplease replace input_specification in quantum_package_input.py with the following:\n') - log('input_specification = obj({') + nxs_print('\nplease replace input_specification in quantum_package_input.py with the following:\n') + nxs_print('input_specification = obj({') s = '' for vpath in sorted(new_input_spec.keys()): vtype = new_input_spec[vpath] s += f" '{vpath}' : {vtype},\n" #end for s += ' })\n' - log(s) + nxs_print(s) #end if #end def extract_input_specification diff --git a/nexus/nexus/tests/test_generic.py b/nexus/nexus/tests/test_generic.py index 977d76ecaa..cdcbc8ff68 100644 --- a/nexus/nexus/tests/test_generic.py +++ b/nexus/nexus/tests/test_generic.py @@ -7,7 +7,7 @@ @isolate_nexus_core def test_logging(): - from ..generic import log,error + from ..generic import nxs_print,error from ..generic import generic_settings,NexusError logfile = generic_settings.devlog @@ -16,23 +16,23 @@ def test_logging(): # simple message s = 'simple message' logfile.reset() - log(s) + nxs_print(s) assert(logfile.s==s+'\n') # list of items items = ['a','b','c',1,2,3] logfile.reset() - log(*items) + nxs_print(*items) assert(logfile.s=='a b c 1 2 3 \n') # message with indentation s = 'a message\nwith indentation' logfile.reset() - log(s,indent=' ') + nxs_print(s,indent=' ') assert(logfile.s==' a message\n with indentation\n') logfile.reset() - log(s,indent='msg: ') + nxs_print(s,indent='msg: ') assert(logfile.s=='msg: a message\nmsg: with indentation\n') # writing to separate log files @@ -41,13 +41,13 @@ def test_logging(): s2 = 'message to log 2' logfile.reset() logfile2.reset() - log(s1) + nxs_print(s1) assert(logfile.s==s1+'\n') assert(logfile2.s=='') logfile.reset() logfile2.reset() - log(s2,logfile=logfile2) + nxs_print(s2,logfile=logfile2) assert(logfile.s=='') assert(logfile2.s==s2+'\n') From 26165e329d49392907772a0afa74243225d7f89b Mon Sep 17 00:00:00 2001 From: brockdyer03 Date: Sat, 5 Sep 2026 12:05:44 -0400 Subject: [PATCH 3/3] Nexus: Make all regular Nexus output go to `sys.stdout` --- nexus/nexus/__init__.py | 1 - nexus/nexus/bin/qmca | 2 +- nexus/nexus/developer.py | 7 ---- nexus/nexus/generic.py | 22 +++-------- nexus/nexus/project_manager.py | 3 +- nexus/nexus/tests/__init__.py | 41 +------------------ nexus/nexus/tests/test_generic.py | 48 +++++++++++------------ nexus/nexus/tests/test_nexus_base.py | 10 ++--- nexus/nexus/tests/test_project_manager.py | 2 - 9 files changed, 36 insertions(+), 100 deletions(-) diff --git a/nexus/nexus/__init__.py b/nexus/nexus/__init__.py index 5fe32a6fe6..cdf1ee2090 100644 --- a/nexus/nexus/__init__.py +++ b/nexus/nexus/__init__.py @@ -29,7 +29,6 @@ from pathlib import Path from .nexus_version import nexus_version -from .generic import generic_settings from .developer import obj, nxs_print, NexusError from .debug import ci from .utilities import path_string diff --git a/nexus/nexus/bin/qmca b/nexus/nexus/bin/qmca index 2d366b5d03..c94ff3414c 100755 --- a/nexus/nexus/bin/qmca +++ b/nexus/nexus/bin/qmca @@ -213,7 +213,7 @@ class QBase(DevBase): text+=str(t)+' ' #end for pad = n*' ' - self._logfile.write(pad+text.replace('\n','\n'+pad)+'\n') + sys.stdout.write(pad+text.replace('\n','\n'+pad)+'\n') #end def log diff --git a/nexus/nexus/developer.py b/nexus/nexus/developer.py index 4ceca3095a..346ea00b2d 100644 --- a/nexus/nexus/developer.py +++ b/nexus/nexus/developer.py @@ -31,7 +31,6 @@ import traceback -from .generic import generic_settings def deprecation_error(): @@ -58,14 +57,8 @@ def __iter__(self): deprecation_error() # change from deepcopy to shallow copy, blow up def copy(self): deprecation_error() - # logging - unique to Nexus-style DevBase (future refactor) - @property - def _logfile(self): - return generic_settings.devlog - def nxs_print(self,*a,**kw): - kw.setdefault('logfile',self._logfile) nxs_print(*a,**kw) def warn(self,msg,indent=' '): diff --git a/nexus/nexus/generic.py b/nexus/nexus/generic.py index 9ee234fbd6..0148c922c8 100644 --- a/nexus/nexus/generic.py +++ b/nexus/nexus/generic.py @@ -24,22 +24,10 @@ import sys import traceback import warnings -from typing import NoReturn, TextIO, ClassVar, TypeAlias +from typing import NoReturn, TextIO, TypeAlias VersionStr: TypeAlias = str -class generic_settings: - devlog = sys.stdout - - # Warnings for trying to reference or set `raise_error` - @property - def raise_error(self): - warn("Referencing `raise_error` is deprecated!", warn_type="dev") - @raise_error.setter - def raise_error(self, _): - warn("Setting `raise_error` is deprecated!", warn_type="dev") -#end class generic_settings - class NexusError(Exception): """Exception for errors that are caused by a bug in Nexus.""" @@ -70,9 +58,9 @@ class NexusUserWarning(NexusDevWarning): # Hook for replacing `warnings.showwarning` -def __nexus_showwarning(message, category, filename, lineno, file=None, line=None): +def __nexus_showwarning(message, category, filename, lineno, file=None, line=None): # noqa: ARG001 if file is None: - file = generic_settings.devlog + file = sys.stdout indent = "" cls = "" @@ -122,7 +110,7 @@ def nxs_print( logfile: TextIO | None = None, n: int = 0 ) -> None: - logfile = logfile if logfile is not None else generic_settings.devlog + logfile = logfile if logfile is not None else sys.stdout if n!=0: if indent is None: indent = n*' ' @@ -149,7 +137,7 @@ def nxs_print( def message(msg,header=None,post_header=' message:',indent=' ',logfile=None): if logfile is None: - logfile = generic_settings.devlog + logfile = sys.stdout #end if if header is None: header = post_header.lstrip() diff --git a/nexus/nexus/project_manager.py b/nexus/nexus/project_manager.py index f7a7a28dc1..58a5ed4698 100644 --- a/nexus/nexus/project_manager.py +++ b/nexus/nexus/project_manager.py @@ -18,6 +18,7 @@ import gc import os +import sys import time from typing import ClassVar,Literal,TextIO from . import memory @@ -360,7 +361,7 @@ def status_line(self,sim,extra=''): if sim.finished: result = 'FAILURE' if sim.failed else 'SUCCESS' #end if - result = color_status_result(result,self._logfile) + result = color_status_result(result,sys.stdout) sline = f'{status} {result:<7} {pid:<8} {sim.identifier:<6} {sim.locdir}' self.nxs_print(sline,extra,n=2) #end def status_line diff --git a/nexus/nexus/tests/__init__.py b/nexus/nexus/tests/__init__.py index 46940472d0..6badc68e10 100644 --- a/nexus/nexus/tests/__init__.py +++ b/nexus/nexus/tests/__init__.py @@ -4,7 +4,6 @@ from copy import deepcopy import functools from nexus.nexus_base import nexus_core, nexus_noncore, nexus_noncore_defaults -from nexus.generic import generic_settings from nexus.pseudoset import PseudoSet from nexus.simulation import Simulation @@ -65,38 +64,6 @@ def restore_nexus_core(nexus_core_storage: dict, nexus_noncore_storage: dict): assert len(nexus_core_storage) == 0, "Nexus NonCore keys have not been properly reset!" -class FakeLog: - def __init__(self): - self.reset() - - def reset(self): - self.s = "" - - def write(self,s): - self.s += s - - def close(self): - None - - def contents(self): - return self.s - - -def divert_nexus_log(): - """Create a fake logging object to divert Nexus's output.""" - logging_storage = { - 'devlog': generic_settings.devlog, - } - logfile = FakeLog() - generic_settings.devlog = logfile - return logfile, logging_storage - - -def restore_nexus_log(logging_storage: dict): - """Restore Nexus's logging to the state stored in ``logging_storage``.""" - generic_settings.devlog = logging_storage.pop('devlog') - - def isolate_nexus_core(test_func = None): """Isolate changes in ``nexus_core`` for a test function.""" @@ -107,17 +74,15 @@ def wrap_path(tmp_path): nexus_core_storage, nexus_noncore_storage = divert_nexus_core() pseudo_files = deepcopy(PseudoSet.pseudo_files) labeled_pseudosets = deepcopy(PseudoSet.labeled_pseudosets) - logfile, logging_storage = divert_nexus_log() try: test_func(tmp_path) test_err = None - except Exception as err: + except Exception as err: # noqa: BLE001 test_err = err restore_nexus_core(nexus_core_storage, nexus_noncore_storage) PseudoSet.pseudo_files = pseudo_files PseudoSet.labeled_pseudosets = labeled_pseudosets - restore_nexus_log(logging_storage) Simulation.clear_all_sims() if test_err is not None: raise test_err @@ -127,17 +92,15 @@ def wrap(): nexus_core_storage, nexus_noncore_storage = divert_nexus_core() pseudo_files = deepcopy(PseudoSet.pseudo_files) labeled_pseudosets = deepcopy(PseudoSet.labeled_pseudosets) - logfile, logging_storage = divert_nexus_log() try: test_func() test_err = None - except Exception as err: + except Exception as err: # noqa: BLE001 test_err = err restore_nexus_core(nexus_core_storage, nexus_noncore_storage) PseudoSet.pseudo_files = pseudo_files PseudoSet.labeled_pseudosets = labeled_pseudosets - restore_nexus_log(logging_storage) Simulation.clear_all_sims() if test_err is not None: raise test_err diff --git a/nexus/nexus/tests/test_generic.py b/nexus/nexus/tests/test_generic.py index cdcbc8ff68..6bbc6470cf 100644 --- a/nexus/nexus/tests/test_generic.py +++ b/nexus/nexus/tests/test_generic.py @@ -2,54 +2,52 @@ from . import NexusTestOrder pytestmark = pytest.mark.order(NexusTestOrder.GENERIC_OPERATION) -from . import isolate_nexus_core, FakeLog +from . import isolate_nexus_core from ..generic import warn, NexusDevWarning, NexusUserWarning, nxs_deprecate +from ..generic import nxs_print,error +from ..generic import NexusError -@isolate_nexus_core -def test_logging(): - from ..generic import nxs_print,error - from ..generic import generic_settings,NexusError - logfile = generic_settings.devlog +def test_logging(tmp_path, capsys): # test log # simple message s = 'simple message' - logfile.reset() nxs_print(s) - assert(logfile.s==s+'\n') + captured = capsys.readouterr() + assert(captured.out==s+'\n') # list of items items = ['a','b','c',1,2,3] - logfile.reset() nxs_print(*items) - assert(logfile.s=='a b c 1 2 3 \n') + captured = capsys.readouterr() + assert(captured.out=='a b c 1 2 3 \n') # message with indentation s = 'a message\nwith indentation' - logfile.reset() nxs_print(s,indent=' ') - assert(logfile.s==' a message\n with indentation\n') + captured = capsys.readouterr() + assert(captured.out==' a message\n with indentation\n') - logfile.reset() nxs_print(s,indent='msg: ') - assert(logfile.s=='msg: a message\nmsg: with indentation\n') + captured = capsys.readouterr() + assert(captured.out=='msg: a message\nmsg: with indentation\n') # writing to separate log files - logfile2 = FakeLog() + logfile = tmp_path / "fake.log" + logfile.touch() s1 = 'message to log 1' s2 = 'message to log 2' - logfile.reset() - logfile2.reset() nxs_print(s1) - assert(logfile.s==s1+'\n') - assert(logfile2.s=='') + captured = capsys.readouterr() + assert(captured.out==s1+'\n') + assert(logfile.read_text()=='') - logfile.reset() - logfile2.reset() - nxs_print(s2,logfile=logfile2) - assert(logfile.s=='') - assert(logfile2.s==s2+'\n') + with open(logfile, "w") as lf: + nxs_print(s2, logfile=lf) + captured = capsys.readouterr() + assert(captured.out=='') + assert(logfile.read_text()==s2+'\n') # test error with pytest.raises(NexusError, match="testing environment"): @@ -61,7 +59,6 @@ def test_logging(): #end def test_logging -@isolate_nexus_core def test_warn(): with pytest.warns(NexusUserWarning, match="This is a test warning"): warn("This is a test warning", warn_type="user") @@ -71,7 +68,6 @@ def test_warn(): #end def test_warn -@isolate_nexus_core def test_nxs_deprecate(): @nxs_deprecate(since="2.3.9", replacement="Some other function") diff --git a/nexus/nexus/tests/test_nexus_base.py b/nexus/nexus/tests/test_nexus_base.py index 358ae8d1d2..f66e6b504c 100644 --- a/nexus/nexus/tests/test_nexus_base.py +++ b/nexus/nexus/tests/test_nexus_base.py @@ -4,7 +4,6 @@ from . import isolate_nexus_core, TEST_DIR from ..testing import object_eq -from ..generic import generic_settings TEST_FILES = { @@ -36,15 +35,14 @@ def test_empty_init(): #end def test_empty_init -@isolate_nexus_core -def test_write_splash(): +def test_write_splash(capsys): from ..nexus_base import write_splash - log = generic_settings.devlog assert(not hasattr(write_splash, "wrote_splash")) write_splash() - assert('Nexus' in log.contents()) - assert('Please cite:' in log.contents()) + captured = capsys.readouterr() + assert('Nexus' in captured.out) + assert('Please cite:' in captured.out) assert(hasattr(write_splash, "wrote_splash")) assert(write_splash.wrote_splash) #end def test_write_splash diff --git a/nexus/nexus/tests/test_project_manager.py b/nexus/nexus/tests/test_project_manager.py index 0e1e03a9b0..884aff4fe2 100644 --- a/nexus/nexus/tests/test_project_manager.py +++ b/nexus/nexus/tests/test_project_manager.py @@ -277,7 +277,6 @@ def test_check_dependencies(): def test_write_simulation_status(capsys): - from ..generic import generic_settings from ..nexus_base import nexus_core from ..simulation import Simulation from ..project_manager import ProjectManager @@ -398,7 +397,6 @@ def isatty(self): @isolate_nexus_core def test_run_project(tmp_path): - from ..generic import generic_settings from ..nexus_base import nexus_core from ..simulation import Simulation,input_template from ..project_manager import ProjectManager