Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6f97bdd

Browse files
committed
Adjust clr loading and merge Mono code into one file
1 parent fabe401 commit 6f97bdd

File tree

7 files changed

+243
-361
lines changed

7 files changed

+243
-361
lines changed

clr.py

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,40 @@
22
Legacy Python.NET loader for backwards compatibility
33
"""
44

5-
def _load():
5+
def _get_netfx_path():
66
import os, sys
7-
import importlib.util as util
87

98
if sys.maxsize > 2 ** 32:
109
arch = "amd64"
1110
else:
1211
arch = "x86"
1312

14-
path = os.path.join(os.path.dirname(__file__), "pythonnet", "dlls", arch, "clr.pyd")
15-
del sys.modules["clr"]
13+
return os.path.join(os.path.dirname(__file__), "pythonnet", "netfx", arch, "clr.pyd")
14+
15+
16+
def _get_mono_path():
17+
import os, glob
18+
19+
paths = glob.glob(os.path.join(os.path.dirname(__file__), "pythonnet", "mono", "clr.*so"))
20+
return paths[0]
21+
22+
23+
def _load_clr():
24+
import sys
25+
from importlib import util
26+
27+
if sys.platform == "win32":
28+
path = _get_netfx_path()
29+
else:
30+
path = _get_mono_path()
31+
32+
del sys.modules[__name__]
1633

1734
spec = util.spec_from_file_location("clr", path)
1835
clr = util.module_from_spec(spec)
1936
spec.loader.exec_module(clr)
2037

21-
sys.modules["clr"] = clr
38+
sys.modules[__name__] = clr
39+
2240

23-
_load()
41+
_load_clr()

pythonnet/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
mono/
2+
netfx/
3+
runtime/

pythonnet/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
def get_assembly_path():
2+
import os
3+
return os.path.dirname(__file__) + "/runtime/Python.Runtime.dll"

setup.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env python
22

3-
from setuptools import setup, find_packages, Command, Extension
3+
from setuptools import setup, Command, Extension
44
from wheel.bdist_wheel import bdist_wheel
55
from setuptools.command.build_ext import build_ext
66
import distutils
@@ -164,18 +164,23 @@ def finalize_options(self):
164164

165165

166166
ext_modules = [
167+
DotnetLib(
168+
"python-runtime",
169+
"src/runtime/Python.Runtime.csproj",
170+
output="pythonnet/runtime"
171+
),
167172
DotnetLib(
168173
"clrmodule-amd64",
169174
"src/clrmodule/",
170175
runtime="win-x64",
171-
output="pythonnet/dlls/amd64",
176+
output="pythonnet/netfx/amd64",
172177
rename={"clr.dll": "clr.pyd"},
173178
),
174179
DotnetLib(
175180
"clrmodule-x86",
176181
"src/clrmodule/",
177182
runtime="win-x86",
178-
output="pythonnet/dlls/x86",
183+
output="pythonnet/netfx/x86",
179184
rename={"clr.dll": "clr.pyd"},
180185
),
181186
]
@@ -192,7 +197,7 @@ def finalize_options(self):
192197
clr_ext = Extension(
193198
"clr",
194199
language="c++",
195-
sources=["src/monoclr/pynetinit.c", "src/monoclr/clrmod.c"],
200+
sources=["src/monoclr/clrmod.c"],
196201
extra_compile_args=cflags.split(" "),
197202
extra_link_args=libs.split(" "),
198203
)
@@ -209,6 +214,7 @@ def finalize_options(self):
209214
license="MIT",
210215
author="The Contributors of the Python.NET Project",
211216
author_email="[email protected]",
217+
packages=["pythonnet"],
212218
setup_requires=["setuptools_scm"],
213219
install_requires=["pycparser"],
214220
long_description=long_description,

src/monoclr/clrmod.c

Lines changed: 203 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,223 @@
1-
#include "pynetclr.h"
1+
// #define Py_LIMITED_API 0x03050000
2+
#include <Python.h>
23

3-
/* List of functions defined in the module */
4-
static PyMethodDef clr_methods[] = {
5-
{NULL, NULL, 0, NULL} /* Sentinel */
6-
};
4+
#include "stdlib.h"
75

8-
PyDoc_STRVAR(clr_module_doc,
9-
"clr facade module to initialize the CLR. It's later "
10-
"replaced by the real clr module. This module has a facade "
11-
"attribute to make it distinguishable from the real clr module."
12-
);
6+
#define MONO_VERSION "v4.0.30319.1"
7+
#define MONO_DOMAIN "Python"
138

14-
static PyNet_Args *pn_args;
15-
char **environ = NULL;
16-
17-
#if PY_MAJOR_VERSION >= 3
18-
static struct PyModuleDef clrdef = {
19-
PyModuleDef_HEAD_INIT,
20-
"clr", /* m_name */
21-
clr_module_doc, /* m_doc */
22-
-1, /* m_size */
23-
clr_methods, /* m_methods */
24-
NULL, /* m_reload */
25-
NULL, /* m_traverse */
26-
NULL, /* m_clear */
27-
NULL, /* m_free */
28-
};
9+
#include <mono/jit/jit.h>
10+
#include <mono/metadata/environment.h>
11+
#include <mono/metadata/mono-config.h>
12+
#include <mono/metadata/debug-helpers.h>
13+
#include <mono/metadata/assembly.h>
14+
15+
#ifndef _WIN32
16+
#include "dirent.h"
17+
#include "dlfcn.h"
18+
#include "libgen.h"
19+
#include "alloca.h"
2920
#endif
3021

31-
static PyObject *_initclr()
22+
typedef struct
3223
{
33-
PyObject *m;
24+
MonoDomain *domain;
25+
MonoAssembly *pr_assm;
26+
MonoMethod *shutdown;
27+
const char *pr_file;
28+
char *error;
29+
char *init_name;
30+
char *shutdown_name;
31+
PyObject *module;
32+
} PyNet_Args;
3433

35-
/* Create the module and add the functions */
36-
#if PY_MAJOR_VERSION >= 3
37-
m = PyModule_Create(&clrdef);
38-
#else
39-
m = Py_InitModule3("clr", clr_methods, clr_module_doc);
40-
#endif
41-
if (m == NULL)
42-
return NULL;
43-
PyModule_AddObject(m, "facade", Py_True);
44-
Py_INCREF(Py_True);
34+
PyNet_Args *PyNet_Init(void);
35+
static PyNet_Args *pn_args;
4536

46-
pn_args = PyNet_Init(1);
37+
PyMODINIT_FUNC
38+
PyInit_clr(void)
39+
{
40+
pn_args = PyNet_Init();
4741
if (pn_args->error)
4842
{
4943
return NULL;
5044
}
5145

52-
if (NULL != pn_args->module)
53-
return pn_args->module;
46+
return pn_args->module;
47+
}
48+
49+
void PyNet_Finalize(PyNet_Args *);
50+
void main_thread_handler(PyNet_Args *user_data);
51+
52+
// initialize Mono and PythonNet
53+
PyNet_Args *PyNet_Init()
54+
{
55+
PyObject *pn_module;
56+
PyObject *pn_path;
57+
PyNet_Args *pn_args;
58+
pn_args = (PyNet_Args *)malloc(sizeof(PyNet_Args));
59+
60+
pn_module = PyImport_ImportModule("pythonnet");
61+
if (pn_module == NULL)
62+
{
63+
pn_args->error = "Failed to import pythonnet";
64+
return pn_args;
65+
}
66+
67+
pn_path = PyObject_CallMethod(pn_module, "get_assembly_path", NULL);
68+
if (pn_path == NULL)
69+
{
70+
Py_DecRef(pn_module);
71+
pn_args->error = "Failed to get assembly path";
72+
return pn_args;
73+
}
5474

55-
return m;
75+
pn_args->pr_file = PyUnicode_AsUTF8(pn_path);
76+
pn_args->error = NULL;
77+
pn_args->shutdown = NULL;
78+
pn_args->module = NULL;
79+
80+
#ifdef __linux__
81+
// Force preload libmono-2.0 as global. Without this, on some systems
82+
// symbols from libmono are not found by libmononative (which implements
83+
// some of the System.* namespaces). Since the only happened on Linux so
84+
// far, we hardcode the library name, load the symbols into the global
85+
// namespace and leak the handle.
86+
dlopen("libmono-2.0.so", RTLD_LAZY | RTLD_GLOBAL);
87+
#endif
88+
89+
pn_args->init_name = "Python.Runtime:InitExt()";
90+
pn_args->shutdown_name = "Python.Runtime:Shutdown()";
91+
92+
pn_args->domain = mono_jit_init_version(MONO_DOMAIN, MONO_VERSION);
93+
94+
// XXX: Skip setting config for now, should be derived from pr_file
95+
// mono_domain_set_config(pn_args->domain, ".", "Python.Runtime.dll.config");
96+
97+
/*
98+
* Load the default Mono configuration file, this is needed
99+
* if you are planning on using the dllmaps defined on the
100+
* system configuration
101+
*/
102+
mono_config_parse(NULL);
103+
104+
/* I can't use this call to run the main_thread_handler. The function
105+
* runs it in another thread but *this* thread holds the Python
106+
* import lock -> DEAD LOCK.
107+
*
108+
* mono_runtime_exec_managed_code(pn_args->domain, main_thread_handler,
109+
* pn_args);
110+
*/
111+
112+
main_thread_handler(pn_args);
113+
114+
if (pn_args->error != NULL)
115+
{
116+
PyErr_SetString(PyExc_ImportError, pn_args->error);
117+
}
118+
return pn_args;
56119
}
57120

58-
#if PY_MAJOR_VERSION >= 3
59-
PyMODINIT_FUNC
60-
PyInit_clr(void)
121+
char *PyNet_ExceptionToString(MonoObject *e);
122+
123+
// Shuts down PythonNet and cleans up Mono
124+
void PyNet_Finalize(PyNet_Args *pn_args)
61125
{
62-
return _initclr();
126+
MonoObject *exception = NULL;
127+
128+
if (pn_args->shutdown)
129+
{
130+
mono_runtime_invoke(pn_args->shutdown, NULL, NULL, &exception);
131+
if (exception)
132+
{
133+
pn_args->error = PyNet_ExceptionToString(exception);
134+
}
135+
pn_args->shutdown = NULL;
136+
}
137+
138+
if (pn_args->domain)
139+
{
140+
mono_jit_cleanup(pn_args->domain);
141+
pn_args->domain = NULL;
142+
}
143+
free(pn_args);
63144
}
64-
#else
65-
PyMODINIT_FUNC
66-
initclr(void)
145+
146+
MonoMethod *getMethodFromClass(MonoClass *cls, char *name)
67147
{
68-
_initclr();
148+
MonoMethodDesc *mdesc;
149+
MonoMethod *method;
150+
151+
mdesc = mono_method_desc_new(name, 1);
152+
method = mono_method_desc_search_in_class(mdesc, cls);
153+
mono_method_desc_free(mdesc);
154+
155+
return method;
156+
}
157+
158+
void main_thread_handler(PyNet_Args *user_data)
159+
{
160+
PyNet_Args *pn_args = user_data;
161+
MonoMethod *init;
162+
MonoImage *pr_image;
163+
MonoClass *pythonengine;
164+
MonoObject *exception = NULL;
165+
MonoObject *init_result;
166+
167+
pn_args->pr_assm = mono_domain_assembly_open(pn_args->domain, pn_args->pr_file);
168+
if (!pn_args->pr_assm)
169+
{
170+
pn_args->error = "Unable to load assembly";
171+
return;
172+
}
173+
174+
pr_image = mono_assembly_get_image(pn_args->pr_assm);
175+
if (!pr_image)
176+
{
177+
pn_args->error = "Unable to get image";
178+
return;
179+
}
180+
181+
pythonengine = mono_class_from_name(pr_image, "Python.Runtime", "PythonEngine");
182+
if (!pythonengine)
183+
{
184+
pn_args->error = "Unable to load class PythonEngine from Python.Runtime";
185+
return;
186+
}
187+
188+
init = getMethodFromClass(pythonengine, pn_args->init_name);
189+
if (!init)
190+
{
191+
pn_args->error = "Unable to fetch Init method from PythonEngine";
192+
return;
193+
}
194+
195+
pn_args->shutdown = getMethodFromClass(pythonengine, pn_args->shutdown_name);
196+
if (!pn_args->shutdown)
197+
{
198+
pn_args->error = "Unable to fetch shutdown method from PythonEngine";
199+
return;
200+
}
201+
202+
init_result = mono_runtime_invoke(init, NULL, NULL, &exception);
203+
if (exception)
204+
{
205+
pn_args->error = PyNet_ExceptionToString(exception);
206+
return;
207+
}
208+
209+
pn_args->module = *(PyObject**)mono_object_unbox(init_result);
210+
}
211+
212+
// Get string from a Mono exception
213+
char *PyNet_ExceptionToString(MonoObject *e)
214+
{
215+
MonoMethodDesc *mdesc = mono_method_desc_new(":ToString()", 0 /*FALSE*/);
216+
MonoMethod *mmethod = mono_method_desc_search_in_class(mdesc, mono_get_object_class());
217+
mono_method_desc_free(mdesc);
218+
219+
mmethod = mono_object_get_virtual_method(e, mmethod);
220+
MonoString *monoString = (MonoString*) mono_runtime_invoke(mmethod, e, NULL, NULL);
221+
mono_runtime_invoke(mmethod, e, NULL, NULL);
222+
return mono_string_to_utf8(monoString);
69223
}
70-
#endif

0 commit comments

Comments
 (0)