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

kunitoki / popsicle / 8371986093

21 Mar 2024 08:38AM UTC coverage: 20.182% (+1.4%) from 18.801%
8371986093

Pull #24

github

kunitoki
More examples with wgpu
Pull Request #24: More tests coverage

324 of 487 new or added lines in 8 files covered. (66.53%)

45 existing lines in 5 files now uncovered.

23834 of 118095 relevant lines covered (20.18%)

3608.02 hits per line

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

44.33
/modules/juce_python/bindings/ScriptJuceGuiEntryPointsBindings.cpp
1
/**
2
 * juce_python - Python bindings for the JUCE framework.
3
 *
4
 * This file is part of the popsicle project.
5
 *
6
 * Copyright (c) 2024 - kunitoki <kunitoki@gmail.com>
7
 *
8
 * popsicle is an open source library subject to commercial or open-source licensing.
9
 *
10
 * By using popsicle, you agree to the terms of the popsicle License Agreement, which can
11
 * be found at https://raw.githubusercontent.com/kunitoki/popsicle/master/LICENSE
12
 *
13
 * Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses).
14
 *
15
 * POPSICLE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER EXPRESSED
16
 * OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE DISCLAIMED.
17
 */
18

19
#include "ScriptJuceGuiEntryPointsBindings.h"
20

21
#define JUCE_PYTHON_INCLUDE_PYBIND11_OPERATORS
22
#define JUCE_PYTHON_INCLUDE_PYBIND11_IOSTREAM
23
#include "../utilities/PyBind11Includes.h"
24

25
#if JUCE_WINDOWS
26
#include "../utilities/WindowsIncludes.h"
27
#endif
28

29
#include "ScriptJuceOptionsBindings.h"
30

31
#include <functional>
32
#include <string_view>
33
#include <typeinfo>
34
#include <tuple>
35

36
// =================================================================================================
37

38
namespace juce {
39

40
#if ! JUCE_WINDOWS
41
 extern const char* const* juce_argv;
42
 extern int juce_argc;
43
#endif
44

45
} // namespace juce
46

47
namespace popsicle::Bindings {
48

49
using namespace juce;
50

51
namespace py = pybind11;
52
using namespace py::literals;
53

54
namespace {
55

56
// ============================================================================================
57

58
void runApplication (JUCEApplicationBase* application, int milliseconds)
×
59
{
60
    {
61
        py::gil_scoped_release release;
×
62

63
        if (! application->initialiseApp())
×
64
            return;
×
65
    }
66

67
    while (! MessageManager::getInstance()->hasStopMessageBeenSent())
×
68
    {
69
        try
70
        {
NEW
71
            py::gil_scoped_release release;
×
72

NEW
73
            MessageManager::getInstance()->runDispatchLoopUntil (milliseconds);
×
74
        }
NEW
75
        catch (const py::error_already_set& e)
×
76
        {
NEW
77
            if (globalOptions().catchExceptionsAndContinue)
×
78
            {
79
                Helpers::printPythonException (e);
×
80
            }
81
            else
82
            {
NEW
83
                throw e;
×
84
            }
85
        }
86

87
        if (globalOptions().caughtKeyboardInterrupt)
×
88
            break;
×
89

90
        if (PyErr_CheckSignals() != 0)
×
91
            throw py::error_already_set();
×
92
    }
93
}
94

95
} // namespace
96

97
void registerJuceGuiEntryPointsBindings (py::module_& m)
1✔
98
{
99
#if ! JUCE_PYTHON_EMBEDDED_INTERPRETER
100

101
    // =================================================================================================
102

103
    m.def ("START_JUCE_APPLICATION", [](py::handle applicationType, bool catchExceptionsAndContinue)
×
104
    {
105
#if JUCE_MAC
106
        juce::Process::setDockIconVisible (true);
107
#endif
108

109
        globalOptions().catchExceptionsAndContinue = catchExceptionsAndContinue;
×
110
        globalOptions().caughtKeyboardInterrupt = false;
×
111

112
        py::scoped_ostream_redirect output;
×
113

114
        if (! applicationType)
×
115
            throw py::value_error("Argument must be a JUCEApplication subclass");
×
116

117
        JUCEApplicationBase* application = nullptr;
×
118

119
        auto sys = py::module_::import ("sys");
×
120
        auto systemExit = [sys, &application]
×
121
        {
122
            const int returnValue = application != nullptr ? application->shutdownApp() : 255;
×
123

124
            sys.attr ("exit") (returnValue);
×
125
        };
×
126

127
#if ! JUCE_WINDOWS
128
        StringArray arguments;
×
129
        for (auto arg : sys.attr ("argv"))
×
130
            arguments.add (arg.cast<String>());
×
131

132
        Array<const char*> argv;
×
133
        for (const auto& arg : arguments)
×
134
            argv.add (arg.toRawUTF8());
×
135

136
        juce_argv = argv.getRawDataPointer();
×
137
        juce_argc = argv.size();
×
138
#endif
139

140
        auto pyApplication = applicationType(); // TODO - error checking (python)
×
141

142
        application = pyApplication.cast<JUCEApplication*>();
×
143
        if (application == nullptr)
×
144
        {
145
            systemExit();
×
146
            return;
×
147
        }
148

149
        try
150
        {
151
            runApplication (application, globalOptions().messageManagerGranularityMilliseconds);
×
152
        }
153
        catch (const py::error_already_set& e)
×
154
        {
155
            Helpers::printPythonException (e);
×
156
        }
157

158
        systemExit();
×
159
    }, "applicationType"_a, "catchExceptionsAndContinue"_a = false);
1✔
160

161
#endif
162

163
    // =================================================================================================
164

165
    struct PyTestableApplication
166
    {
167
        struct Scope
168
        {
169
            Scope (py::handle applicationType)
42✔
170
            {
42✔
171
                if (! applicationType)
42✔
172
                    throw py::value_error("Argument must be a JUCEApplication subclass");
×
173

174
                JUCEApplicationBase* application = nullptr;
42✔
175

176
#if ! JUCE_WINDOWS
177
                for (auto arg : py::module_::import ("sys").attr ("argv"))
168✔
178
                    arguments.add (arg.cast<String>());
126✔
179

180
                for (const auto& arg : arguments)
168✔
181
                    argv.add (arg.toRawUTF8());
126✔
182

183
                juce_argv = argv.getRawDataPointer();
42✔
184
                juce_argc = argv.size();
42✔
185
#endif
186

187
                auto pyApplication = applicationType();
42✔
188

189
                application = pyApplication.cast<JUCEApplication*>();
42✔
190
                if (application == nullptr)
42✔
191
                    return;
×
192

193
                if (! application->initialiseApp())
42✔
194
                    return;
×
195
            }
196

197
            ~Scope()
42✔
198
            {
42✔
199
            }
42✔
200

201
        private:
202
#if ! JUCE_WINDOWS
203
            StringArray arguments;
204
            Array<const char*> argv;
205
#endif
206
        };
207

208
        PyTestableApplication (py::handle applicationType)
42✔
209
            : applicationType (applicationType)
42✔
210
        {
211
        }
42✔
212

213
        void processEvents(int milliseconds = 20)
141✔
214
        {
215
            try
216
            {
217
                JUCE_TRY
218
                {
219
                    py::gil_scoped_release release;
141✔
220

221
                    if (MessageManager::getInstance()->hasStopMessageBeenSent())
141✔
222
                        return;
×
223

224
                    MessageManager::getInstance()->runDispatchLoopUntil (milliseconds);
141✔
225
                }
226
                JUCE_CATCH_EXCEPTION
×
227

228
                bool isErrorSignalInFlight = PyErr_CheckSignals() != 0;
141✔
229
                if (isErrorSignalInFlight)
141✔
230
                    throw py::error_already_set();
×
231
            }
232
            catch (const py::error_already_set& e)
×
233
            {
234
                py::print (e.what());
×
235
            }
236
            catch (...)
×
237
            {
238
                py::print ("unhandled runtime error");
×
239
            }
240
        }
241

242
        py::handle applicationType;
243
        std::unique_ptr<Scope> applicationScope;
244
    };
245

246
    py::class_<PyTestableApplication> classTestableApplication (m, "TestApplication");
1✔
247

248
    classTestableApplication
249
        .def (py::init<py::handle>())
1✔
250
        .def ("processEvents", &PyTestableApplication::processEvents, "milliseconds"_a = 20)
2✔
251
        .def ("__enter__", [](PyTestableApplication& self)
42✔
252
        {
253
            self.applicationScope = std::make_unique<PyTestableApplication::Scope> (self.applicationType);
42✔
254
            return std::addressof (self);
42✔
255
        }, py::return_value_policy::reference)
1✔
256
        .def ("__exit__", [](PyTestableApplication& self, const std::optional<py::type>&, const std::optional<py::object>&, const std::optional<py::object>&)
42✔
257
        {
258
            self.applicationScope.reset();
42✔
259
        })
1✔
260
        .def ("__next__", [](PyTestableApplication& self)
126✔
261
        {
262
            self.processEvents();
126✔
263
            return std::addressof (self);
126✔
264
        }, py::return_value_policy::reference)
1✔
265
    ;
266
}
1✔
267

268
} // namespace popsicle::Bindings
269

270
// =================================================================================================
271

272
#if ! JUCE_PYTHON_EMBEDDED_INTERPRETER && JUCE_WINDOWS
273
BOOL APIENTRY DllMain(HANDLE instance, DWORD reason, LPVOID reserved)
274
{
275
    juce::ignoreUnused (reserved);
276

277
    if (reason == DLL_PROCESS_ATTACH)
278
        juce::Process::setCurrentModuleInstanceHandle (instance);
279

280
    return true;
281
}
282
#endif
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

© 2025 Coveralls, Inc