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

nasa / trick / 31182405870

07 Aug 2026 01:23PM UTC coverage: 56.739% (+0.07%) from 56.671%
31182405870

push

github

web-flow
build(deps): bump brace-expansion in /trick_source/web/dashboard (#2183)

Bumps  and [brace-expansion](https://github.com/juliangruber/brace-expansion). These dependencies needed to be updated together.

Updates `brace-expansion` from 1.1.13 to 1.1.18
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/v1.1.13...v1.1.18)

Updates `brace-expansion` from 2.0.3 to 2.1.4
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/v1.1.13...v1.1.18)

---
updated-dependencies:
- dependency-name: brace-expansion
  dependency-version: 1.1.18
  dependency-type: indirect
- dependency-name: brace-expansion
  dependency-version: 2.1.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

14868 of 26204 relevant lines covered (56.74%)

462593.6 hits per line

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

80.22
/trick_source/sim_services/InputProcessor/IPPython.cpp
1
/*
2
   PURPOSE: ( Python input processor )
3
   REFERENCE: ( Trick Simulation Environment )
4
   ASSUMPTIONS AND LIMITATIONS: ( None )
5
   CLASS: ( N/A )
6
   LIBRARY DEPENDENCY: ( None )
7
   PROGRAMMERS: ( Alex Lin NASA 2009 )
8
*/
9

10
#include <Python.h>
11

12
#include "trick/IPPython.hh"
13

14
#include "trick/MemoryManager.hh"
15
#include "trick/exec_proto.h"
16
#include "trick/io_alloc.h"
17
#include "trick/message_proto.h"
18
#include "trick/message_type.h"
19

20
#include <atomic>
21
#include <cstdio>
22
#include <cstdlib>
23
#include <cstring>
24
#include <iostream>
25
#include <pthread.h>
26
#include <sstream>
27
#include <string>
28
#include <unistd.h>
29

30
Trick::IPPython * the_pip ;
31

32
//Constructor
33
Trick::IPPython::IPPython() { the_pip = this; }
188✔
34

35
// Need to save the state of the main thread to allow child threads to run PyRun variants.
36
static PyThreadState *_save = NULL;
37

38
// Number of threads currently inside parse()/parse_condition(), i.e. threads that hold
39
// the Python GIL or are waiting to acquire it. See GILGuard and shutdown() below.
40
static std::atomic<int> active_parse_count(0);
41

42
namespace
43
{
44
    /**
45
     * Holds the Python GIL for a scope, with three guarantees that the bare
46
     * PyGILState_Ensure()/PyGILState_Release() pair does not provide.
47
     *
48
     * 1. Thread cancellation is disabled for as long as the GIL is held. Without this a
49
     *    pthread_cancel() (ThreadBase::cancel_thread(), used during shutdown) can destroy the
50
     *    thread between Ensure and Release, leaving the GIL owned by a thread that no longer
51
     *    exists. Nobody can ever reacquire it after that. A cancel requested while
52
     *    cancellation is disabled simply stays pending and is delivered once the GIL has been
53
     *    released, which is exactly the behaviour we want.
54
     *
55
     * 2. The GIL is released even if an exception unwinds out of the scope. Trick's
56
     *    exec_terminate() throws Trick::ExecutiveException from inside PyRun_SimpleString
57
     *    (VariableServerSessionThread_loop.cpp catches it), and the previous straight-line
58
     *    code skipped PyGILState_Release() on that path, leaking the GIL.
59
     *
60
     * 3. active_parse_count tracks how many threads are in the interpreter, so that
61
     *    IPPython::shutdown() can tell whether reacquiring the GIL is safe or would block
62
     *    forever.
63
     *
64
     * The GIL is held for the guard's whole lifetime, so put the guard in a nested scope if
65
     * later code in the same function must run without it. To take the GIL twice in one
66
     * function, use two separate blocks; reusing the variable name is fine. Dropping the GIL
67
     * in between lets other threads change what you are reading, so only split when you
68
     * actually want to yield.
69
     *
70
     * Nesting guards is also legal: PyGILState_Ensure() is reentrant, and a nested call
71
     * returns PyGILState_LOCKED, so the inner Release() does not drop the GIL. That only
72
     * holds because each guard keeps its own gstate; the handles must not be shared
73
     * between calls.
74
     */
75
    class GILGuard
76
    {
77
        public:
78
            GILGuard()
5,707✔
79
            {
5,707✔
80
                pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancel_state);
5,707✔
81
                // Incremented before Ensure() so that a thread blocked *waiting* for the GIL
82
                // is counted too. Erring towards "someone is in the interpreter" is the safe
83
                // direction: the worst case is that we skip Py_Finalize() unnecessarily.
84
                ++active_parse_count;
5,707✔
85
                gstate = PyGILState_Ensure();
5,707✔
86
            }
5,707✔
87

88
            ~GILGuard()
5,707✔
89
            {
90
                PyGILState_Release(gstate);
5,707✔
91
                --active_parse_count;
5,707✔
92
                pthread_setcancelstate(old_cancel_state, NULL);
5,707✔
93
            }
5,707✔
94

95
            GILGuard(const GILGuard&)            = delete;
96
            GILGuard& operator=(const GILGuard&) = delete;
97

98
        private:
99
            PyGILState_STATE gstate;
100
            int old_cancel_state;
101
    };
102

103
}
104

105
/**
106
@details
107
-# Loops through all of the memorymanager allocations testing if a name handle was given.
108
 -# If a name and a user type_name were given to the allocation
109
  -# If the user_type_name is not a Trick core class, prefixed with "Trick::"
110
  -# Create a python statement to assign the python name to an address: <name> = trick.castAsTYPE(int(<address>))
111
  -# Run the statement in the python interpreter
112
*/
113
void Trick::IPPython::get_TMM_named_variables() {
199✔
114
    //std::cout << "top level names at initialization" << std::endl ;
115
    Trick::ALLOC_INFO_MAP_ITER aim_it ;
199✔
116
    for ( aim_it = trick_MM->alloc_info_map_begin() ; aim_it != trick_MM->alloc_info_map_end() ; ++aim_it ) {
5,580✔
117
        ALLOC_INFO * alloc_info = (*aim_it).second ;
5,381✔
118
        if ( alloc_info->name != NULL and alloc_info->user_type_name != NULL ) {
5,381✔
119
            std::stringstream ss ;
5,130✔
120
            std::string user_type_name = alloc_info->user_type_name ;
5,130✔
121
            size_t start_colon ;
122
            while ( ( start_colon = user_type_name.find("::") ) != std::string::npos ) {
5,741✔
123
                user_type_name.replace( start_colon , 2 , "__" ) ;
611✔
124
            }
125
            // The castAs method may not exist if the class was hidden from SWIG (#ifndef SWIG).
126
            // Use a try/except block to test if the method exists or not.  If it doesn't exist
127
            // don't worry about it.  Also only assign python variable if it is pointing to
128
            // something python doesn't owns.  Otherwise we could free the object we're trying to assign.
129
            ss << "try:" << std::endl ;
5,130✔
130
            ss << "    if '" << alloc_info->name << "' not in globals() or " ;
5,130✔
131
            ss << alloc_info->name << ".thisown == False:" <<  std::endl ;
5,130✔
132
            ss << "        " << alloc_info->name << " = " ;
5,130✔
133
            ss << "trick.castAs" << user_type_name << "(int(" << alloc_info->start << "))" << std::endl ;
5,130✔
134
            ss << "except AttributeError:" << std::endl ;
5,130✔
135
            ss << "    pass" << std::endl ;
5,130✔
136
            GILGuard gil_guard;
5,130✔
137
            PyRun_SimpleString(ss.str().c_str());
5,130✔
138
        }
5,130✔
139
    }
140
}
199✔
141

142
//Initialize and run the Python input processor on the user input file.
143
int Trick::IPPython::init() {
188✔
144
    /** @par Detailed Design: */
145

146
    FILE *input_fp ;
147
    int ret ;
148
    std::string error_message ;
188✔
149

150
    // Run Py_Initialze first for python 2.x
151
#if PY_VERSION_HEX < 0x03000000
152
    Py_Initialize();
153
#endif
154

155
    /* Run the Swig generated routine in S_source_wrap.cpp. */
156
    init_swig_modules() ;
188✔
157

158
    // Run Py_Initialze after init_swig_modules for python 3.x
159
#if PY_VERSION_HEX >= 0x03000000
160
    Py_Initialize();
188✔
161
#endif
162

163
    // The following PyRun_ calls do not require the PyGILState guards because no threads are launched
164
    /* Import simulation specific routines into interpreter. */
165
    PyRun_SimpleString(
188✔
166
     "import sys\n"
167
     "import os\n"
168
     "import struct\n"
169
     "import binascii\n"
170
     "if 'VIRTUAL_ENV' in os.environ:\n"
171
     "    sys.path.append(os.path.join(os.environ['VIRTUAL_ENV'], \"lib\", f\"python{sys.version_info.major}.{sys.version_info.minor}\", \"site-packages\"))\n"
172
     "sys.path.append(os.getcwd() + '/trick.zip')\n"
173
     "sys.path.append(os.path.join(os.environ['TRICK_HOME'], 'share/trick/pymods'))\n"
174
     "sys.path += map(str.strip, os.environ['TRICK_PYTHON_PATH'].split(':'))\n"
175
     "import trick\n"
176
     "sys.path.append(os.getcwd() + \"/Modified_data\")\n"
177
    ) ;
178

179
    /* Make shortcut names for all known sim_objects. */
180
    get_TMM_named_variables() ;
188✔
181

182
    /* An input file is not required, if the name is empty just return. */
183
    if ( input_file.empty() ) {
188✔
184
        return(0) ;
×
185
    }
186

187
    if ((input_fp = fopen(input_file.c_str(), "r")) == NULL) {
188✔
188
        error_message = "No input file found named " + input_file ;
×
189
        exec_terminate_with_return(-1 , __FILE__ , __LINE__ , error_message.c_str() ) ;
×
190
    }
191

192
    /* Read and parse the input file. */
193
    if ( verify_input ) {
188✔
194
        PyRun_SimpleString("sys.settrace(trick.traceit)") ;
×
195
    }
196

197
    /* Read and parse the input file. */
198
    if ( save_input ) {
188✔
199
        PyRun_SimpleString("trick.open_input_file_log()") ;
×
200
        PyRun_SimpleString("sys.settrace(trick.traceittofile)") ;
×
201
    }
202

203
    if ( (ret = PyRun_SimpleFile(input_fp, input_file.c_str())) !=  0 ) {
188✔
204
        exec_terminate_with_return(ret , __FILE__ , __LINE__ , "Input Processor error\n" ) ;
×
205
    }
206

207
    if ( verify_input ) {
186✔
208
       std::stringstream ss ;
×
209
       ss << "import hashlib" << std::endl ;
×
210
       ss << "input_file = " << "'" << input_file.c_str() << "'" << std::endl;
×
211
       ss << "print('{0} SHA1: {1}'.format(input_file,hashlib.sha1(open(input_file, 'rb').read()).hexdigest()))" << std::endl ;
×
212
       PyRun_SimpleString(ss.str().c_str()) ;
×
213
       exec_terminate_with_return(ret , __FILE__ , __LINE__ , "Input file verification complete\n" ) ;
×
214
    }
×
215

216
    if ( save_input ) {
186✔
217
        PyRun_SimpleString("trick.close_input_file_log()") ;
×
218
        PyRun_SimpleString("sys.settrace(None)") ;
×
219
    }
220

221
    fclose(input_fp) ;
186✔
222

223
    // Release the GIL from the main thread.
224
    Py_UNBLOCK_THREADS
186✔
225

226
    return(0) ;
186✔
227
}
188✔
228

229
//Command to parse the given string.
230
int Trick::IPPython::parse(std::string in_string) {
521✔
231

232
    int ret ;
233
    in_string += "\n" ;
521✔
234

235
    GILGuard gil_guard;
521✔
236
    ret = PyRun_SimpleString(in_string.c_str());
521✔
237

238
    return ret ;
521✔
239

240
}
521✔
241

242
/**
243
 @details
244
 The incoming statement is assumed to be a conditional fragment, i.e. "a > b".  We need
245
 to get the return value of this fragment by setting the return value of it to a known
246
 variable name in the input processor.  We can then assign that return value to the
247
 incoming return_value reference.
248

249
-# Lock the input processor mutex
250
-# Create a complete statement that assigns the conditional fragment to our return value
251
-# parse the condition
252
-# copy the return value to the incoming cond_return_value
253
-# Unlock the input processor mutex
254
*/
255
int Trick::IPPython::parse_condition(std::string in_string, int & cond_return_val ) {
56✔
256

257
    in_string =  std::string("trick_ip.ip.return_val = ") + in_string + "\n" ;
56✔
258
    // Running the simple string will set return_val.
259
    int py_ret;
260
    {
261
        GILGuard gil_guard;
56✔
262
        py_ret = PyRun_SimpleString(in_string.c_str());
56✔
263
    }
56✔
264
    cond_return_val = return_val ;
56✔
265

266
    return py_ret ;
56✔
267

268
}
269

270
//Restart job that reloads event_list from checkpointable structures
271
int Trick::IPPython::restart() {
11✔
272
    /* Make shortcut names for all known sim_objects. */
273
    get_TMM_named_variables() ;
11✔
274
    return 0 ;
11✔
275
}
276

277
int Trick::IPPython::shutdown() {
155✔
278

279
    if ( Py_IsInitialized() ) {
155✔
280
        // If any thread is still inside parse()/parse_condition() then it owns the GIL,
281
        // or is queued for it, and may never give it back -- the usual cause is a
282
        // variable server command that blocks inside a model call, since SWIG does not
283
        // release the GIL around wrapped calls. Reacquiring the GIL below would then
284
        // block forever.
285
        //
286
        // Skipping Py_Finalize() in that case is safe. The process is about to exit, so
287
        // the OS reclaims the interpreter's memory regardless; all we give up is Python's
288
        // orderly teardown (atexit handlers, __del__ methods). That is a far better
289
        // outcome than wedging the sim, and it lets Executive::shutdown() carry on and
290
        // report the real return code instead of the run appearing to hang.
291
        int in_interpreter = active_parse_count;
155✔
292
        if (in_interpreter > 0)
155✔
293
        {
294
            message_publish(
×
295
                MSG_WARNING,
296
                "Trick::IPPython::shutdown() skipping Py_Finalize(): %d thread(s) still executing in the Python "
297
                "interpreter and holding the GIL. The Python interpreter will not be finalized.\n",
298
                in_interpreter);
299
            return (0);
×
300
        }
301

302
        // Obtain the GIL so that we can shut down properly
303
        // Check if the thread state is actually saved before trying to restore it
304
        // Python thread state is NULL when using JIT Input
305
        if (_save != NULL)
155✔
306
        {
307
            Py_BLOCK_THREADS _save = NULL;
155✔
308
        }
309
        Py_Finalize();
155✔
310
    }
311
    return(0) ;
155✔
312
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc