Skip to content

Commit cf8f3c9

Browse files
committed
python 增加对Namespace的检查
1 parent fb26c22 commit cf8f3c9

File tree

3 files changed

+172
-6
lines changed

3 files changed

+172
-6
lines changed

unity/Assets/core/upm/Runtime/Src/Backends/BackendPython.cs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ import types
6969
from importlib import machinery
7070
7171
_csTypeCache_ = dict()
72-
72+
_p_loader = scriptEnv.GetLoader()
7373
7474
class PesapiLoader(importlib.abc.Loader):
7575
@@ -78,7 +78,14 @@ def exec_module(self, mod):
7878
7979
def create_module(self, spec: machinery.ModuleSpec):
8080
type_name = spec.name
81-
return puerts.load_type(type_name)
81+
if _p_loader.get_NamespaceManager().IsValidNamespace(type_name):
82+
return NameSpaceProxy(type_name)
83+
else:
84+
result = puerts.load_type(type_name)
85+
if result is not None:
86+
return result
87+
else:
88+
raise ModuleNotFoundError(f'No namespace or type named {type_name}')
8289
8390
8491
class PesapiFinder(importlib.abc.MetaPathFinder):
@@ -94,16 +101,24 @@ def __init__(self, namespace_name: str):
94101
self.__p_namespace_name = namespace_name
95102
96103
def __getattr__(self, attr: str):
97-
return puerts.load_type(self.__p_namespace_name + '.' + attr)
104+
full_name = self.__p_namespace_name + '.' + attr
105+
result = puerts.load_type(full_name)
106+
if result is not None:
107+
return result
108+
else:
109+
if _p_loader.get_NamespaceManager().IsValidNamespace(full_name):
110+
return NameSpaceProxy(full_name)
111+
else:
112+
raise ModuleNotFoundError(f'No namespace or type named {full_name}')
98113
99114
100115
class puerts:
101116
@staticmethod
102117
def load_type(type_name: str):
103118
""""""
104-
Load a C# class or generic type definition, or return a namespace proxy if the type is not found.
119+
Load a C# class or generic type definition, or return None if the type is not found.
105120
:param type_name: The full name of the C# type to load. If the type is generic, use the format 'TypeName__Tn' where n is the number of generic parameters.
106-
:return: The loaded C# class or generic type definition, or a namespace proxy if the type is not found.
121+
:return: The loaded C# class or generic type definition, or None if the type is not found.
107122
""""""
108123
generic_tick_index = type_name.find('__T')
109124
if generic_tick_index != -1:
@@ -114,7 +129,8 @@ def load_type(type_name: str):
114129
return _csTypeCache_[type_name]
115130
cs_type = scriptEnv.GetTypeByString(type_name)
116131
if cs_type is None:
117-
return NameSpaceProxy(type_name)
132+
print('Type not found: ' + type_name)
133+
return None
118134
if cs_type.IsGenericTypeDefinition:
119135
# cache generic type definitions directly
120136
_csTypeCache_[type_name] = cs_type

unity/Assets/core/upm/Runtime/Src/Loaders/PythonLoader.cs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,16 @@
66
*/
77

88
using System;
9+
using System.Collections.Generic;
10+
using System.Linq;
11+
using System.Reflection;
912

1013
namespace Puerts
1114
{
1215
public class PythonLoader : ILoader
1316
{
17+
public static NamespaceManager NamespaceManager { get; } = new NamespaceManager();
18+
1419
public bool FileExists(string filepath)
1520
{
1621
throw new NotImplementedException();
@@ -22,6 +27,83 @@ public string ReadFile(string filepath, out string debugpath)
2227
}
2328
}
2429

30+
public class NamespaceManager
31+
{
32+
private readonly HashSet<string> _namespaces = new HashSet<string>();
33+
private static readonly object _lock = new object();
34+
35+
public NamespaceManager()
36+
{
37+
AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoad;
38+
LoadNamespacesFromLoadedAssemblies();
39+
}
40+
41+
private void LoadNamespacesFromLoadedAssemblies()
42+
{
43+
lock (_lock)
44+
{
45+
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
46+
foreach (var assembly in assemblies)
47+
{
48+
AddNamespacesFromAssembly(assembly);
49+
}
50+
}
51+
}
52+
53+
private void OnAssemblyLoad(object sender, AssemblyLoadEventArgs args)
54+
{
55+
lock (_lock)
56+
{
57+
AddNamespacesFromAssembly(args.LoadedAssembly);
58+
}
59+
}
60+
61+
private void AddNamespacesFromAssembly(Assembly assembly)
62+
{
63+
try
64+
{
65+
var types = assembly.GetTypes();
66+
foreach (var type in types)
67+
{
68+
if (!string.IsNullOrEmpty(type.Namespace))
69+
{
70+
_namespaces.Add(type.Namespace);
71+
}
72+
}
73+
}
74+
catch (ReflectionTypeLoadException ex)
75+
{
76+
foreach (var type in ex.Types)
77+
{
78+
if (type != null && !string.IsNullOrEmpty(type.Namespace))
79+
{
80+
_namespaces.Add(type.Namespace);
81+
}
82+
}
83+
}
84+
catch (Exception ex)
85+
{
86+
Console.WriteLine($"Error on loading assembly {assembly.FullName}: {ex.Message}");
87+
}
88+
}
89+
90+
public IEnumerable<string> GetAllNamespaces()
91+
{
92+
lock (_lock)
93+
{
94+
return _namespaces.OrderBy(ns => ns).ToList();
95+
}
96+
}
97+
98+
public bool IsValidNamespace(string namespaceName)
99+
{
100+
lock (_lock)
101+
{
102+
return _namespaces.Contains(namespaceName);
103+
}
104+
}
105+
}
106+
25107
public class PythonDefaultLoader : PythonLoader
26108
{
27109
}

unity/test/Src/Cases/Python/CSharpModuleTest.cs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,74 @@ public void AccessInnerClass()
8787
Assert.AreEqual(3, result);
8888
pythonEnv.Dispose();
8989
}
90+
91+
[Test]
92+
public void NameSpaceAccessTest()
93+
{
94+
var pythonEnv = new ScriptEnv(new BackendPython());
95+
// 1 level namespace
96+
pythonEnv.Eval(@"
97+
exec('''
98+
import System.Console
99+
''')
100+
");
101+
// 2 level namespace
102+
pythonEnv.Eval(@"
103+
exec('''
104+
import System.Diagnostics
105+
''')
106+
");
107+
// 2 level namespace with class
108+
pythonEnv.Eval(@"
109+
exec('''
110+
import System.Diagnostics.Debug
111+
''')
112+
");
113+
// 2 level namespace with NameSpaceProxy Access
114+
pythonEnv.Eval(@"
115+
exec('''
116+
import System.Diagnostics
117+
System.Diagnostics.Debug.WriteLine('Test')
118+
''')
119+
");
120+
121+
// 2 level namespace with NameSpaceProxy Access
122+
pythonEnv.Eval(@"
123+
exec('''
124+
import System.Diagnostics as Diagnostics
125+
Diagnostics.Debug.WriteLine('Test')
126+
''')
127+
");
128+
129+
try
130+
{
131+
pythonEnv.Eval(@"
132+
exec('''
133+
import System.Diagnostics
134+
System.Diagnostics.Hello
135+
''')
136+
");
137+
}
138+
catch (Exception e)
139+
{
140+
Assert.True(e.Message.Contains("ModuleNotFoundError: No namespace or type named System.Diagnostics.Hello"), "Unexpected error message" + e.Message);
141+
}
142+
143+
try
144+
{
145+
pythonEnv.Eval(@"
146+
exec('''
147+
import System.Diagnostics as Diagnostics
148+
Diagnostics.Hello
149+
''')
150+
");
151+
}
152+
catch (Exception e)
153+
{
154+
Assert.True(e.Message.Contains("ModuleNotFoundError: No namespace or type named System.Diagnostics.Hello"), "Unexpected error message" + e.Message);
155+
}
156+
pythonEnv.Dispose();
157+
}
90158
/*
91159
[Test]
92160
public void GenericMethodTest()

0 commit comments

Comments
 (0)