@@ -74,51 +74,77 @@ def __str__(self):
74
74
def create ():
75
75
"""Return a new (idle) Python interpreter."""
76
76
id = _interpreters .create (reqrefs = True )
77
- return Interpreter (id )
77
+ return Interpreter (id , _ownsref = True )
78
78
79
79
80
80
def list_all ():
81
81
"""Return all existing interpreters."""
82
- return [Interpreter (id )
83
- for id , in _interpreters .list_all ()]
82
+ return [Interpreter (id , _whence = whence )
83
+ for id , whence in _interpreters .list_all (require_ready = True )]
84
84
85
85
86
86
def get_current ():
87
87
"""Return the currently running interpreter."""
88
- id , = _interpreters .get_current ()
89
- return Interpreter (id )
88
+ id , whence = _interpreters .get_current ()
89
+ return Interpreter (id , _whence = whence )
90
90
91
91
92
92
def get_main ():
93
93
"""Return the main interpreter."""
94
- id , = _interpreters .get_main ()
95
- return Interpreter (id )
94
+ id , whence = _interpreters .get_main ()
95
+ assert whence == _interpreters .WHENCE_RUNTIME , repr (whence )
96
+ return Interpreter (id , _whence = whence )
96
97
97
98
98
99
_known = weakref .WeakValueDictionary ()
99
100
100
101
class Interpreter :
101
- """A single Python interpreter."""
102
+ """A single Python interpreter.
102
103
103
- def __new__ (cls , id , / ):
104
+ Attributes:
105
+
106
+ "id" - the unique process-global ID number for the interpreter
107
+ "whence" - indicates where the interpreter was created
108
+
109
+ If the interpreter wasn't created by this module
110
+ then any method that modifies the interpreter will fail,
111
+ i.e. .close(), .prepare_main(), .exec(), and .call()
112
+ """
113
+
114
+ _WHENCE_TO_STR = {
115
+ _interpreters .WHENCE_UNKNOWN : 'unknown' ,
116
+ _interpreters .WHENCE_RUNTIME : 'runtime init' ,
117
+ _interpreters .WHENCE_LEGACY_CAPI : 'legacy C-API' ,
118
+ _interpreters .WHENCE_CAPI : 'C-API' ,
119
+ _interpreters .WHENCE_XI : 'cross-interpreter C-API' ,
120
+ _interpreters .WHENCE_STDLIB : '_interpreters module' ,
121
+ }
122
+
123
+ def __new__ (cls , id , / , _whence = None , _ownsref = None ):
104
124
# There is only one instance for any given ID.
105
125
if not isinstance (id , int ):
106
126
raise TypeError (f'id must be an int, got { id !r} ' )
107
127
id = int (id )
128
+ if _whence is None :
129
+ if _ownsref :
130
+ _whence = _interpreters .WHENCE_STDLIB
131
+ else :
132
+ _whence = _interpreters .whence (id )
133
+ assert _whence in cls ._WHENCE_TO_STR , repr (_whence )
134
+ if _ownsref is None :
135
+ _ownsref = (_whence == _interpreters .WHENCE_STDLIB )
108
136
try :
109
137
self = _known [id ]
110
138
assert hasattr (self , '_ownsref' )
111
139
except KeyError :
112
- # This may raise InterpreterNotFoundError:
113
- _interpreters .incref (id )
114
- try :
115
- self = super ().__new__ (cls )
116
- self ._id = id
117
- self ._ownsref = True
118
- except BaseException :
119
- _interpreters .decref (id )
120
- raise
140
+ self = super ().__new__ (cls )
121
141
_known [id ] = self
142
+ self ._id = id
143
+ self ._whence = _whence
144
+ self ._ownsref = _ownsref
145
+ if _ownsref :
146
+ # This may raise InterpreterNotFoundError:
147
+ _interpreters .incref (id )
122
148
return self
123
149
124
150
def __repr__ (self ):
@@ -143,33 +169,40 @@ def _decref(self):
143
169
return
144
170
self ._ownsref = False
145
171
try :
146
- _interpreters .decref (self .id )
172
+ _interpreters .decref (self ._id )
147
173
except InterpreterNotFoundError :
148
174
pass
149
175
150
176
@property
151
177
def id (self ):
152
178
return self ._id
153
179
180
+ @property
181
+ def whence (self ):
182
+ return self ._WHENCE_TO_STR [self ._whence ]
183
+
154
184
def is_running (self ):
155
185
"""Return whether or not the identified interpreter is running."""
156
186
return _interpreters .is_running (self ._id )
157
187
188
+ # Everything past here is available only to interpreters created by
189
+ # interpreters.create().
190
+
158
191
def close (self ):
159
192
"""Finalize and destroy the interpreter.
160
193
161
194
Attempting to destroy the current interpreter results
162
195
in an InterpreterError.
163
196
"""
164
- return _interpreters .destroy (self ._id )
197
+ return _interpreters .destroy (self ._id , restrict = True )
165
198
166
199
def prepare_main (self , ns = None , / , ** kwargs ):
167
200
"""Bind the given values into the interpreter's __main__.
168
201
169
202
The values must be shareable.
170
203
"""
171
204
ns = dict (ns , ** kwargs ) if ns is not None else kwargs
172
- _interpreters .set___main___attrs (self ._id , ns )
205
+ _interpreters .set___main___attrs (self ._id , ns , restrict = True )
173
206
174
207
def exec (self , code , / ):
175
208
"""Run the given source code in the interpreter.
@@ -189,7 +222,7 @@ def exec(self, code, /):
189
222
that time, the previous interpreter is allowed to run
190
223
in other threads.
191
224
"""
192
- excinfo = _interpreters .exec (self ._id , code )
225
+ excinfo = _interpreters .exec (self ._id , code , restrict = True )
193
226
if excinfo is not None :
194
227
raise ExecutionFailed (excinfo )
195
228
@@ -209,7 +242,7 @@ def call(self, callable, /):
209
242
# XXX Support args and kwargs.
210
243
# XXX Support arbitrary callables.
211
244
# XXX Support returning the return value (e.g. via pickle).
212
- excinfo = _interpreters .call (self ._id , callable )
245
+ excinfo = _interpreters .call (self ._id , callable , restrict = True )
213
246
if excinfo is not None :
214
247
raise ExecutionFailed (excinfo )
215
248
0 commit comments