Skip to content

Commit 61f0308

Browse files
committed
More work on python coverage
1 parent 7288642 commit 61f0308

File tree

5 files changed

+117
-3
lines changed

5 files changed

+117
-3
lines changed

modules/yup_python/modules/yup_YupInternal_module.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ PYBIND11_EMBEDDED_MODULE(__yup__, m)
4141
py::class_<CustomOutputStream> classCustomOutputStream (m, "__stdout__");
4242
classCustomOutputStream.def_static ("write", [](py::object buffer) { std::cout << buffer.cast<std::string>(); });
4343
classCustomOutputStream.def_static ("flush", [] { std::cout << std::flush; });
44+
classCustomOutputStream.def_static ("isatty", [] { return true; });
4445

4546
struct CustomErrorStream
4647
{
@@ -52,6 +53,7 @@ PYBIND11_EMBEDDED_MODULE(__yup__, m)
5253
py::class_<CustomErrorStream> classCustomErrorStream (m, "__stderr__");
5354
classCustomErrorStream.def_static ("write", [](py::object buffer) { std::cerr << buffer.cast<std::string>(); });
5455
classCustomErrorStream.def_static ("flush", [] { std::cerr << std::flush; });
56+
classCustomErrorStream.def_static ("isatty", [] { return true; });
5557

5658
m.def ("__redirect__", []
5759
{

modules/yup_python/scripting/yup_ScriptEngine.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ std::unique_ptr<PyConfig> ScriptEngine::prepareScriptingHome (
105105
auto mis = MemoryInputStream (mb.getData(), mb.getSize(), false);
106106

107107
auto zip = ZipFile (mis);
108-
zip.uncompressTo (pythonFolder);
108+
zip.uncompressTo (pythonFolder.getParentDirectory());
109109
}
110110

111111
auto config = std::make_unique<PyConfig>();
@@ -133,10 +133,11 @@ ScriptEngine::ScriptEngine (std::unique_ptr<PyConfig> config)
133133
}
134134

135135
ScriptEngine::ScriptEngine (StringArray modules, std::unique_ptr<PyConfig> config)
136-
: customModules (std::move (modules))
136+
: currentConfig (std::move (config))
137+
, customModules (std::move (modules))
137138
{
138139
if (config)
139-
pybind11::initialize_interpreter (config.get(), 0, nullptr, true);
140+
pybind11::initialize_interpreter (currentConfig.get(), 0, nullptr, true);
140141
else
141142
pybind11::initialize_interpreter();
142143

@@ -152,6 +153,16 @@ ScriptEngine::~ScriptEngine()
152153

153154
//==============================================================================
154155

156+
File ScriptEngine::getScriptingHome() const
157+
{
158+
if (currentConfig != nullptr)
159+
return String (currentConfig->home);
160+
161+
return {};
162+
}
163+
164+
//==============================================================================
165+
155166
Result ScriptEngine::runScript (const String& code, py::dict locals, py::dict globals)
156167
{
157168
currentScriptCode = code;

modules/yup_python/scripting/yup_ScriptEngine.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,19 @@ class YUP_API ScriptEngine
103103

104104
//==============================================================================
105105

106+
/** Get the scripting home directory.
107+
108+
When the scripting home is prepared using the prepareScriptingHome method,
109+
this method will return the destination folder used to prepare the home.
110+
This home folder can be used as either `PYTHONHOME` or `--prefix` when installing
111+
python packages using pip.
112+
113+
@return The scripting home directory if present, or an empty `File` if not.
114+
*/
115+
File getScriptingHome() const;
116+
117+
//==============================================================================
118+
106119
/** Prepare a valid python home and return the config to use.
107120
108121
@param programName The desired program name.
@@ -119,6 +132,7 @@ class YUP_API ScriptEngine
119132
private:
120133
Result runScriptInternal (const String& code, pybind11::dict locals, pybind11::dict globals);
121134

135+
std::unique_ptr<PyConfig> currentConfig;
122136
StringArray customModules;
123137
String currentScriptCode;
124138
File currentScriptFile;

python/tools/ArchivePythonStdlib.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ def make_archive(file, directory):
7171
"site-packages",
7272
"test",
7373
"turtledemo",
74+
"EXTERNALLY-MANAGED",
7475
"LICENSE.txt",
7576
]
7677

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
==============================================================================
3+
4+
This file is part of the YUP library.
5+
Copyright (c) 2025 - [email protected]
6+
7+
YUP is an open source library subject to open-source licensing.
8+
9+
The code included in this file is provided under the terms of the ISC license
10+
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
11+
to use, copy, modify, and/or distribute this software for any purpose with or
12+
without fee is hereby granted provided that the above copyright notice and
13+
this permission notice appear in all copies.
14+
15+
YUP IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
16+
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
17+
DISCLAIMED.
18+
19+
==============================================================================
20+
*/
21+
22+
#include <yup_python/yup_python.h>
23+
24+
#include <gtest/gtest.h>
25+
26+
#include <PythonStandardLibrary.h>
27+
28+
using namespace yup;
29+
30+
class ScriptPythonTest : public ::testing::Test
31+
{
32+
protected:
33+
static File getPytestTestFolder()
34+
{
35+
return File (__FILE__)
36+
.getParentDirectory()
37+
.getParentDirectory()
38+
.getParentDirectory()
39+
.getChildFile ("python")
40+
.getChildFile ("tests");
41+
}
42+
43+
void SetUp() override
44+
{
45+
engine = std::make_unique<ScriptEngine> (ScriptEngine::prepareScriptingHome (
46+
YUPApplication::getInstance()->getApplicationName(),
47+
File::getSpecialLocation (File::tempDirectory),
48+
[] (const char*) -> MemoryBlock
49+
{
50+
return { PythonStandardLibrary_data, PythonStandardLibrary_size };
51+
}));
52+
}
53+
54+
void TearDown() override
55+
{
56+
// Cleanup after each test
57+
}
58+
59+
std::unique_ptr<ScriptEngine> engine;
60+
};
61+
62+
TEST_F (ScriptPythonTest, RunPythonTests)
63+
{
64+
auto script = String (R"(
65+
import importlib
66+
67+
package = 'pytest'
68+
69+
try:
70+
importlib.import_module(package)
71+
except ImportError:
72+
import pip
73+
pip.main(['install', package, '--prefix', '{{root_path}}'])
74+
finally:
75+
globals()[package] = importlib.import_module(package)
76+
77+
pytest.main(['-x', '{{test_path}}', '-vv'])
78+
)")
79+
.dedentLines()
80+
.replace ("{{root_path}}", engine->getScriptingHome().getFullPathName())
81+
.replace ("{{test_path}}", getPytestTestFolder().getFullPathName());
82+
83+
auto result = engine->runScript (script);
84+
85+
EXPECT_TRUE (result.wasOk()) << "Pytest failed: " << result.getErrorMessage();
86+
}

0 commit comments

Comments
 (0)