Skip to content

Commit

Permalink
Bug fix include rel paths (#28)
Browse files Browse the repository at this point in the history
* Fix an inconsistency with Nastran when following include statements.

If an INCLUDE statement only specifies a filename (i.e. INCLUDE 'file.bdf'), Nastran treats file.bdf as if it's in the same directory as the file with the INCLUDE statement.

If an INCLUDE statement includes path components (i.e. INCLUDE 'dir1/file.bdf' or INCLUDE '../file.bdf') Nastran treats the path as if it's relative to the directory containing the root Nastran input deck.

Previously, rdcards and rdsets always used the second method.  This was fixed.

* Make a copy of kwargs dictionary before recursive call.

* Correct docstring.

---------

Co-authored-by: Josh Ayers <[email protected]>
  • Loading branch information
joshayers and Josh Ayers authored Oct 1, 2023
1 parent b130eaf commit 28fb168
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 18 deletions.
49 changes: 32 additions & 17 deletions pyyeti/nastran/bulk.py
Original file line number Diff line number Diff line change
Expand Up @@ -416,9 +416,18 @@ def _rdinclude(fiter, s, func, kwargs):
else: # end not found, add entire line
path_parts.append(s.strip())
rel_path = "".join(path_parts)
current_dir_path, root_dir_path = kwargs["include_root_dirs"]
path, symbol_found = _check_for_symbols(rel_path, kwargs["include_symbols"])
if not symbol_found:
path = os.path.abspath(os.path.join(kwargs["include_root_dir"], rel_path))
if os.path.dirname(rel_path) == "":
# rel_path is just a filename, it is relative to current file
dir_path = current_dir_path
else:
# rel_path includes directories, it is relative to root directory
dir_path = root_dir_path
path = os.path.abspath(os.path.join(dir_path, rel_path))
kwargs = kwargs.copy() # make a copy for recursive call
kwargs["include_root_dirs"] = (os.path.dirname(path), root_dir_path)
# read next line, which will be returned by next fiter.send(True) call
fiter.send(False)
return func(path, **kwargs)
Expand Down Expand Up @@ -555,7 +564,7 @@ def rdcards(
keep_comments=False,
follow_includes=True,
include_symbols=None,
include_root_dir=None,
include_root_dirs=None,
):
r"""
Read Nastran cards (lines) into a matrix, dictionary, or list.
Expand Down Expand Up @@ -626,7 +635,7 @@ def rdcards(
include_symbols : dict; optional
A dictionary mapping Nastran symbols to an associated path.
These can be read from a file using :func:`rdsymbols`.
include_root_dir : None; optional
include_root_dirs : None; optional
This parameter is only used when this function is called
recursively while following INCLUDE statements. Users should
keep it as the default value of None.
Expand Down Expand Up @@ -741,15 +750,18 @@ def rdcards(
)
# save root dir from original call, pass through to _rdinclude for recursive calls
try:
include_root_dir = (
os.path.dirname(os.path.abspath(f.name))
if include_root_dir is None
else include_root_dir
include_root_dirs = (
(
os.path.dirname(os.path.abspath(f.name)),
os.path.dirname(os.path.abspath(f.name)),
)
if include_root_dirs is None
else include_root_dirs
)
except AttributeError:
# StringIO object, doesn't make sense to follow includes
follow_includes = False
include_root_dir = None
include_root_dirs = None
include_symbols = (
{symbol.lower(): path for symbol, path in include_symbols.items()}
if include_symbols is not None
Expand All @@ -769,7 +781,7 @@ def rdcards(
"keep_comments": keep_comments,
"follow_includes": follow_includes,
"include_symbols": include_symbols,
"include_root_dir": include_root_dir,
"include_root_dirs": include_root_dirs,
}

if return_var == "dict":
Expand Down Expand Up @@ -885,7 +897,7 @@ def _rdset(fiter, line):


@guitools.read_text_file
def rdsets(f, follow_includes=True, include_symbols=None, include_root_dir=None):
def rdsets(f, follow_includes=True, include_symbols=None, include_root_dirs=None):
"""
Read case control SET statements from a file.
Expand All @@ -904,7 +916,7 @@ def rdsets(f, follow_includes=True, include_symbols=None, include_root_dir=None)
include_symbols : dict; optional
A dictionary mapping Nastran symbols to an associated path.
These can be read from a file using :func:`rdsymbols`.
include_root_dir : None; optional
include_root_dirs : None; optional
This parameter is only used when this function is called
recursively while following INCLUDE statements. Users should
keep it as the default value of None.
Expand All @@ -925,15 +937,18 @@ def rdsets(f, follow_includes=True, include_symbols=None, include_root_dir=None)
"""
# save root dir from original call, pass through to _rdinclude for recursive calls
try:
include_root_dir = (
os.path.dirname(os.path.abspath(f.name))
if include_root_dir is None
else include_root_dir
include_root_dirs = (
(
os.path.dirname(os.path.abspath(f.name)),
os.path.dirname(os.path.abspath(f.name)),
)
if include_root_dirs is None
else include_root_dirs
)
except AttributeError:
# StringIO object, doesn't make sense to follow includes
follow_includes = False
include_root_dir = None
include_root_dirs = None
include_symbols = (
{symbol.lower(): path for symbol, path in include_symbols.items()}
if include_symbols is not None
Expand All @@ -945,7 +960,7 @@ def rdsets(f, follow_includes=True, include_symbols=None, include_root_dir=None)
kwargs = {
"follow_includes": follow_includes,
"include_symbols": include_symbols,
"include_root_dir": include_root_dir,
"include_root_dirs": include_root_dirs,
}

sets = {}
Expand Down
49 changes: 48 additions & 1 deletion pyyeti/tests/test_nastran.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,8 @@ def check_results(cards):
_wtfile(file1_path, file1.replace("file2", "../file2"))
# update to use relative path
# note that path is relative to file1, even though the statement is in
# file2, this is unexpected but is consistent with Nastran
# file2, this is consistent with Nastran when the INCLUDE has directories
# in addition to a filename
_wtfile(
os.path.join(tempdir_path, "file2.bdf"), file2.replace("file3", "../file3")
)
Expand All @@ -243,6 +244,52 @@ def check_results(cards):
)
check_results(cards)

# file1 in subdirectory, file2 and file3 in parent dir
with tempfile.TemporaryDirectory() as tempdir_path:
subdir_path = os.path.join(tempdir_path, "subdir")
os.mkdir(subdir_path)
file1_path = os.path.join(subdir_path, "file1.bdf")
# update to use relative path
_wtfile(file1_path, file1.replace("file2", "../file2"))
# In file2, the "INCLUDE 'file3.bdf'" statement has no directories, so file3 is
# treated as if it's in the same directory as file2.
_wtfile(os.path.join(tempdir_path, "file2.bdf"), file2)
_wtfile(os.path.join(tempdir_path, "file3.bdf"), file3)
# follow_includes is True
cards = nastran.rdcards(
file1_path,
"grid",
blank=None,
return_var="list",
keep_name=True,
follow_includes=True,
)
check_results(cards)

# file1 and file3 in parent_dir, file2 in subdirectory
with tempfile.TemporaryDirectory() as tempdir_path:
file1_path = os.path.join(tempdir_path, "file1.bdf")
file3_path = os.path.join(tempdir_path, "file3.bdf")
subdir_path = os.path.join(tempdir_path, "subdir")
os.mkdir(subdir_path)
file2_path = os.path.join(subdir_path, "file2.bdf")
# file2 and file3 are both included from file1
file1_mod = file1.replace(
"INCLUDE 'file2.\nbdf'", "INCLUDE 'subdir/file2.bdf'\nINCLUDE 'file3.bdf'"
)
_wtfile(file1_path, file1_mod)
_wtfile(file2_path, file2.replace("INCLUDE", "$ INCLUDE"))
_wtfile(file3_path, file3)
cards = nastran.rdcards(
file1_path,
"grid",
blank=None,
return_var="list",
keep_name=True,
follow_includes=True,
)
check_results(cards)

# file1 in parent_dir, file2 and file3 in subdirectory, includes use symbols
with tempfile.TemporaryDirectory() as tempdir_path:
file1_path = os.path.join(tempdir_path, "file1.bdf")
Expand Down

0 comments on commit 28fb168

Please sign in to comment.