• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

idaholab / MontePy / 13886055450

16 Mar 2025 06:10PM UTC coverage: 98.106% (+0.3%) from 97.83%
13886055450

Pull #698

github

web-flow
Merge e92c136f6 into 895002fc1
Pull Request #698: Fixing syntax error with updating is_reflecting

3 of 3 new or added lines in 2 files covered. (100.0%)

130 existing lines in 31 files now uncovered.

7718 of 7867 relevant lines covered (98.11%)

0.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.0
/montepy/input_parser/input_syntax_reader.py
1
# Copyright 2024-2025, Battelle Energy Alliance, LLC All Rights Reserved.
2
from collections import deque
1✔
3
import itertools
1✔
4
import io
1✔
5
import re
1✔
6
import os
1✔
7
import warnings
1✔
8

9
from montepy.constants import *
1✔
10
from montepy.errors import *
1✔
11
from montepy.input_parser.block_type import BlockType
1✔
12
from montepy.input_parser.input_file import MCNP_InputFile
1✔
13
from montepy.input_parser.mcnp_input import Input, Message, ReadInput, Title
1✔
14
from montepy.input_parser.read_parser import ReadParser
1✔
15
from montepy.utilities import is_comment
1✔
16

17

18
reading_queue = deque()
1✔
19

20

21
def read_input_syntax(input_file, mcnp_version=DEFAULT_VERSION, replace=True):
1✔
22
    """Creates a generator function to return a new MCNP input for
23
    every new one that is encountered.
24

25
    This is meant to just handle the MCNP input syntax, it does not
26
    semantically parse the inputs.
27

28
    The version must be a three component tuple e.g., (6, 2, 0) and (5, 1, 60).
29

30
    Parameters
31
    ----------
32
    input_file : MCNP_InputFile
33
        the path to the input file to be read
34
    mcnp_version : tuple
35
        The version of MCNP that the input is intended for.
36
    replace : bool
37
        replace all non-ASCII characters with a space (0x20)
38

39
    Returns
40
    -------
41
    generator
42
        a generator of MCNP_Object objects
43
    """
44
    global reading_queue
45
    reading_queue = deque()
1✔
46
    if input_file.is_stream:
1✔
47
        context = input_file
1✔
48
    else:
49
        context = input_file.open("r", replace=replace)
1✔
50
    with context as fh:
1✔
51
        yield from read_front_matters(fh, mcnp_version)
1✔
52
        yield from read_data(fh, mcnp_version)
1✔
53

54

55
def read_front_matters(fh, mcnp_version):
1✔
56
    """Reads the beginning of an MCNP file for all of the unusual data there.
57

58
    This is a generator function that will yield multiple :class:`MCNP_Input` instances.
59

60
    Warnings
61
    --------
62
    This function will move the file handle forward in state.
63

64
    Warnings
65
    --------
66
    This function will not close the file handle.
67

68
    Parameters
69
    ----------
70
    fh : MCNP_InputFile
71
        The file handle of the input file.
72
    mcnp_version : tuple
73
        The version of MCNP that the input is intended for.
74

75
    Returns
76
    -------
77
    MCNP_Object
78
        an instance of the Title class, and possible an instance of a
79
        Message class
80
    """
81
    is_in_message_block = False
1✔
82
    found_title = False
1✔
83
    lines = []
1✔
84
    raw_lines = []
1✔
85
    for i, line in enumerate(fh):
1✔
86
        if i == 0 and line.upper().startswith("MESSAGE:"):
1✔
87
            is_in_message_block = True
1✔
88
            raw_lines.append(line.rstrip())
1✔
89
            lines.append(line[9:])  # removes "MESSAGE: "
1✔
90
        elif is_in_message_block:
1✔
91
            if line.strip():
1✔
92
                raw_lines.append(line.rstrip())
1✔
93
                lines.append(line)
1✔
94
            # message block is terminated by a blank line
95
            else:
96
                yield Message(raw_lines, lines)
1✔
97
                is_in_message_block = False
1✔
98
        # title always follows complete message, or is first
99
        else:
100
            yield Title([line], line)
1✔
101
            break
1✔
102

103

104
def read_data(fh, mcnp_version, block_type=None, recursion=False):
1✔
105
    """Reads the bulk of an MCNP file for all of the MCNP data.
106

107
    This is a generator function that will yield multiple :class:`MCNP_Input` instances.
108

109
    Warnings
110
    --------
111
    This function will move the file handle forward in state.
112

113
    Warnings
114
    --------
115
    This function will not close the file handle.
116

117
    Parameters
118
    ----------
119
    fh : MCNP_InputFile
120
        The file handle of the input file.
121
    mcnp_version : tuple
122
        The version of MCNP that the input is intended for.
123
    block_type : BlockType
124
        The type of block this file is in. This is only used with
125
        partial files read using the ReadInput.
126
    recursion : bool
127
        Whether or not this is being called recursively. If True this
128
        has been called from read_data. This prevents the reading queue
129
        causing infinite recursion.
130

131
    Returns
132
    -------
133
    MCNP_Input
134
        MCNP_Input instances: Inputs that represent the data in the MCNP
135
        input.
136
    """
137
    current_file = fh
1✔
138
    line_length = get_max_line_length(mcnp_version)
1✔
139
    block_counter = 0
1✔
140
    if block_type is None:
1✔
141
        block_type = BlockType.CELL
1✔
142
    continue_input = False
1✔
143
    has_non_comments = False
1✔
144
    input_raw_lines = []
1✔
145

146
    def flush_block():
1✔
147
        nonlocal block_counter, block_type
148
        if len(input_raw_lines) > 0:
1✔
149
            yield from flush_input()
1✔
150
        block_counter += 1
1✔
151
        if block_counter < 3:
1✔
152
            block_type = BlockType(block_counter)
1✔
153

154
    def flush_input():
1✔
155
        nonlocal input_raw_lines
156
        start_line = current_file.lineno + 1 - len(input_raw_lines)
1✔
157
        input = Input(
1✔
158
            input_raw_lines,
159
            block_type,
160
            current_file,
161
            start_line,
162
        )
163
        try:
1✔
164
            read_input = ReadInput(
1✔
165
                input_raw_lines, block_type, current_file, start_line
166
            )
167
            reading_queue.append((block_type, read_input.file_name, current_file.path))
1✔
168
            yield None
1✔
169
        except ValueError as e:
1✔
170
            if isinstance(e, ParsingError):
1✔
UNCOV
171
                raise e
×
172
            yield input
1✔
173
        continue_input = False
1✔
174
        input_raw_lines = []
1✔
175

176
    for line in fh:
1✔
177
        line = line.expandtabs(TABSIZE)
1✔
178
        line_is_comment = is_comment(line)
1✔
179
        # transition to next block with blank line
180
        if not line.strip():
1✔
181
            yield from flush_block()
1✔
182
            has_non_comments = False
1✔
183
            continue
1✔
184
        # if a new input
185
        if (
1✔
186
            line[0:BLANK_SPACE_CONTINUE].strip()
187
            and not continue_input
188
            and not line_is_comment
189
            and has_non_comments
190
            and input_raw_lines
191
        ):
192
            yield from flush_input()
1✔
193
        # die if it is a vertical syntax format
194
        if "#" in line[0:BLANK_SPACE_CONTINUE] and not line_is_comment:
1✔
195
            raise UnsupportedFeature("Vertical Input format is not allowed")
1✔
196
        # cut line down to allowed length
197
        old_line = line
1✔
198
        line = line[:line_length]
1✔
199
        if len(old_line) != len(line):
1✔
200
            if len(line.split("$")[0]) >= line_length and not COMMENT_FINDER.match(
1✔
201
                line
202
            ):
203
                warnings.warn(
1✔
204
                    f"The line: {old_line} exceeded the allowed line length of: {line_length} for MCNP {mcnp_version}",
205
                    LineOverRunWarning,
206
                )
207
            # if extra length is a comment keep it long
208
            else:
209
                line = old_line
1✔
210
        if line.endswith(" &\n"):
1✔
211
            continue_input = True
1✔
212
        else:
213
            continue_input = False
1✔
214
        has_non_comments = has_non_comments or not line_is_comment
1✔
215
        input_raw_lines.append(line.rstrip())
1✔
216
    yield from flush_block()
1✔
217

218
    if not recursion:
1✔
219
        path = os.path.dirname(fh.name)
1✔
220
        while reading_queue:
1✔
221
            block_type, file_name, parent = reading_queue.popleft()
1✔
222
            new_wrapper = MCNP_InputFile(os.path.join(path, file_name), parent)
1✔
223
            with new_wrapper.open("r") as sub_fh:
1✔
224
                new_wrapper = MCNP_InputFile(file_name, parent)
1✔
225
                for input in read_data(sub_fh, mcnp_version, block_type, True):
1✔
226
                    yield input
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc