Skip to content

[Code scan] Make RunAbacus input-file discovery restore cwd on parse failures #33

Description

@njzjz

This issue is a result of a Codex global code scan of deepmodeling/fpop at commit b05b337590c31a5237b2dcbd9c0833b841c08cd4.

Relevant code:

fpop/fpop/abacus.py

Lines 249 to 305 in b05b337

def get_pporbdpks_from_stru(stru: str= "STRU"):
"read the label, pp, orb, cell, coord, deepks-descriptor"
ABACUS_STRU_KEY_WORD = [
"ATOMIC_SPECIES",
"NUMERICAL_ORBITAL",
"LATTICE_CONSTANT",
"LATTICE_VECTORS",
"ATOMIC_POSITIONS",
"NUMERICAL_DESCRIPTOR",
]
if not os.path.isfile(stru):
return {}
with open(stru) as f1: lines = f1.readlines()
def get_block(keyname):
block = []
for i,line in enumerate(lines):
if line.strip() == "": continue
elif line.split('#')[0].strip() == keyname:
for ij in range(i+1,len(lines)):
if lines[ij].strip() == "": continue
elif lines[ij].strip() in ABACUS_STRU_KEY_WORD:
return block
else:
block.append(lines[ij])
return block
return None
atomic_species = get_block("ATOMIC_SPECIES")
numerical_orbital = get_block("NUMERICAL_ORBITAL")
dpks = get_block("NUMERICAL_DESCRIPTOR")
dpks = None if dpks == None else dpks[0].strip()
#read species
pp = []
labels = []
mass = []
if atomic_species:
for line in atomic_species:
sline = line.split()
labels.append(sline[0])
pp.append(sline[2])
mass.append(float(sline[1]))
#read orbital
if numerical_orbital == None:
orb = None
else:
orb = []
for line in numerical_orbital:
orb.append(line.split()[0])
return {
"labels": labels,
"mass": mass,
"pp":pp,
"orb":orb,
"dpks":dpks,}

fpop/fpop/abacus.py

Lines 556 to 596 in b05b337

class RunAbacus(RunFp):
def input_files(self,task_path) -> List[str]:
r'''The mandatory input files to run an abacus task.
Returns
-------
files: List[str]
A list of madatory input files names.
'''
cwd = os.getcwd()
os.chdir(task_path)
files = ["INPUT","STRU"]
if os.path.isfile("KPT"):
files.append("KPT")
files_tmp = []
#read STRU
stru_data = get_pporbdpks_from_stru("STRU")
if stru_data != None:
orb_files = stru_data["orb"]
pp_files = stru_data["pp"]
dpks_descriptor = stru_data["dpks"]
files_tmp += pp_files
if orb_files: files_tmp += orb_files
if dpks_descriptor: files_tmp += [dpks_descriptor]
#read INPUT
input = AbacusInputs.read_inputf("INPUT")
if "deepks_model" in input: files_tmp += [input["deepks_model"]]
for ii in files_tmp:
if os.path.isfile(ii):
files.append(ii)
else:
print("ERROR: file %s is not found" % ii)
os.chdir(cwd)
return files

Problem:
RunAbacus.input_files() changes the process working directory with os.chdir(task_path) and restores it only on the successful path. If STRU parsing raises, the caller is left in the task directory.

There are at least two easy ways to trigger this:

  1. If STRU is missing, get_pporbdpks_from_stru("STRU") returns {}. input_files() checks if stru_data != None, then indexes stru_data["orb"] and raises KeyError.
  2. If a STRU section header has an inline comment, such as NUMERICAL_ORBITAL # comment, get_block() does not strip comments when detecting the next section header. The commented header is treated as an ATOMIC_SPECIES data line and parsing can fail with ValueError: could not convert string to float: '#'.

Minimal reproduction for the second case:

ATOMIC_SPECIES
H 1 H.upf
NUMERICAL_ORBITAL # comment
H.orb
LATTICE_CONSTANT
1

Expected behavior:
RunAbacus.input_files() should restore cwd in a finally block, and STRU parsing failures should be reported as clear missing/invalid input errors instead of leaking process state.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    Status
    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions