1
- # It's recommended to run this with `python3 -I not_impl_gen.py`, to make sure
2
- # that nothing in your global Python environment interferes with what's being
3
- # extracted here.
4
- #
1
+ #!/usr/bin/env python3 -I
2
+
5
3
# This script generates Lib/snippets/whats_left_data.py with these variables defined:
6
4
# expected_methods - a dictionary mapping builtin objects to their methods
7
5
# cpymods - a dictionary mapping module names to their contents
8
6
# libdir - the location of RustPython's Lib/ directory.
9
7
10
- import inspect
11
- import io
8
+ #
9
+ # TODO: include this:
10
+ # which finds all modules it has available and
11
+ # creates a Python dictionary mapping module names to their contents, which is
12
+ # in turn used to generate a second Python script that also finds which modules
13
+ # it has available and compares that against the first dictionary we generated.
14
+ # We then run this second generated script with RustPython.
15
+
16
+ import argparse
17
+ import re
12
18
import os
13
19
import re
14
20
import sys
21
+ import json
15
22
import warnings
23
+ import inspect
24
+ import subprocess
25
+ import platform
16
26
from pydoc import ModuleScanner
17
27
28
+ if not sys .flags .isolated :
29
+ print ("running without -I option." )
30
+ print ("python -I whats_left.py" )
31
+ exit (1 )
32
+
33
+ GENERATED_FILE = "extra_tests/snippets/not_impl.py"
34
+
35
+ implementation = platform .python_implementation ()
36
+ if implementation != "CPython" :
37
+ sys .exit ("whats_left.py must be run under CPython, got {implementation} instead" )
38
+
39
+
40
+ def parse_args ():
41
+ parser = argparse .ArgumentParser (description = "Process some integers." )
42
+ parser .add_argument (
43
+ "--signature" ,
44
+ action = "store_true" ,
45
+ help = "print functions whose signatures don't match CPython's" ,
46
+ )
47
+ parser .add_argument (
48
+ "--json" ,
49
+ action = "store_true" ,
50
+ help = "print output as JSON (instead of line by line)" ,
51
+ )
52
+
53
+ args = parser .parse_args ()
54
+ return args
55
+
56
+
57
+ args = parse_args ()
58
+
18
59
19
60
# modules suggested for deprecation by PEP 594 (www.python.org/dev/peps/pep-0594/)
20
61
# some of these might be implemented, but they are not a priority
54
95
'_testbuffer' , '_testcapi' , '_testimportmultiple' , '_testinternalcapi' , '_testmultiphase' ,
55
96
}
56
97
57
- IGNORED_MODULES = {' this' , ' antigravity' } | PEP_594_MODULES | CPYTHON_SPECIFIC_MODS
98
+ IGNORED_MODULES = {" this" , " antigravity" } | PEP_594_MODULES | CPYTHON_SPECIFIC_MODS
58
99
59
100
sys .path = [
60
101
path
@@ -188,6 +229,7 @@ def onerror(modname):
188
229
def import_module (module_name ):
189
230
import io
190
231
from contextlib import redirect_stdout
232
+
191
233
# Importing modules causes ('Constant String', 2, None, 4) and
192
234
# "Hello world!" to be printed to stdout.
193
235
f = io .StringIO ()
@@ -203,6 +245,7 @@ def import_module(module_name):
203
245
204
246
def is_child (module , item ):
205
247
import inspect
248
+
206
249
item_mod = inspect .getmodule (item )
207
250
return item_mod is module
208
251
@@ -250,7 +293,7 @@ def gen_modules():
250
293
output += gen_methods ()
251
294
output += f"""
252
295
cpymods = { gen_modules ()!r}
253
- libdir = { os .path .abspath ("../ Lib/" ).encode ('utf8' )!r}
296
+ libdir = { os .path .abspath ("Lib/" ).encode ('utf8' )!r}
254
297
255
298
"""
256
299
@@ -260,7 +303,7 @@ def gen_modules():
260
303
extra_info ,
261
304
dir_of_mod_or_error ,
262
305
import_module ,
263
- is_child
306
+ is_child ,
264
307
]
265
308
for fn in REUSED :
266
309
output += "" .join (inspect .getsourcelines (fn )[0 ]) + "\n \n "
@@ -278,7 +321,7 @@ def compare():
278
321
import sys
279
322
import warnings
280
323
from contextlib import redirect_stdout
281
-
324
+ import json
282
325
import platform
283
326
284
327
def method_incompatability_reason (typ , method_name , real_method_value ):
@@ -288,7 +331,7 @@ def method_incompatability_reason(typ, method_name, real_method_value):
288
331
289
332
is_inherited = not attr_is_not_inherited (typ , method_name )
290
333
if is_inherited :
291
- return "inherited"
334
+ return "( inherited) "
292
335
293
336
value = extra_info (getattr (typ , method_name ))
294
337
if value != real_method_value :
@@ -321,16 +364,20 @@ def method_incompatability_reason(typ, method_name, real_method_value):
321
364
322
365
rustpymods = {mod : dir_of_mod_or_error (mod ) for mod in mod_names }
323
366
324
- not_implemented = {}
325
- failed_to_import = {}
326
- missing_items = {}
327
- mismatched_items = {}
367
+ result = {
368
+ "cpython_modules" : {},
369
+ "implemented" : {},
370
+ "not_implemented" : {},
371
+ "failed_to_import" : {},
372
+ "missing_items" : {},
373
+ "mismatched_items" : {},
374
+ }
328
375
for modname , cpymod in cpymods .items ():
329
376
rustpymod = rustpymods .get (modname )
330
377
if rustpymod is None :
331
- not_implemented [modname ] = None
378
+ result [ " not_implemented" ] [modname ] = None
332
379
elif isinstance (rustpymod , Exception ):
333
- failed_to_import [modname ] = rustpymod
380
+ result [ " failed_to_import" ] [modname ] = rustpymod . __class__ . __name__ + str ( rustpymod )
334
381
else :
335
382
implemented_items = sorted (set (cpymod ) & set (rustpymod ))
336
383
mod_missing_items = set (cpymod ) - set (rustpymod )
@@ -343,48 +390,18 @@ def method_incompatability_reason(typ, method_name, real_method_value):
343
390
if rustpymod [item ] != cpymod [item ]
344
391
and not isinstance (cpymod [item ], Exception )
345
392
]
346
- if mod_missing_items :
347
- missing_items [modname ] = mod_missing_items
348
- if mod_mismatched_items :
349
- mismatched_items [modname ] = mod_mismatched_items
350
-
351
- # missing entire module
352
- print ("# modules" )
353
- for modname in not_implemented :
354
- print (modname , "(entire module)" )
355
- for modname , exception in failed_to_import .items ():
356
- print (f"{ modname } (exists but not importable: { exception } )" )
357
-
358
- # missing from builtins
359
- print ("\n # builtin items" )
360
- for module , missing_methods in not_implementeds .items ():
361
- for method , reason in missing_methods .items ():
362
- print (f"{ module } .{ method } " + (f" ({ reason } )" if reason else "" ))
363
-
364
- # missing from modules
365
- print ("\n # stdlib items" )
366
- for modname , missing in missing_items .items ():
367
- for item in missing :
368
- print (item )
369
-
370
- print ("\n # mismatching signatures (warnings)" )
371
- for modname , mismatched in mismatched_items .items ():
372
- for (item , rustpy_value , cpython_value ) in mismatched :
373
- if cpython_value == "ValueError('no signature found')" :
374
- continue # these items will never match
375
- print (f"{ item } { rustpy_value } != { cpython_value } " )
393
+ if mod_missing_items or mod_mismatched_items :
394
+ if mod_missing_items :
395
+ result ["missing_items" ][modname ] = mod_missing_items
396
+ if mod_mismatched_items :
397
+ result ["mismatched_items" ][modname ] = mod_mismatched_items
398
+ else :
399
+ result ["implemented" ][modname ] = None
376
400
377
- result = {
378
- "not_implemented" : not_implemented ,
379
- "failed_to_import" : failed_to_import ,
380
- "missing_items" : missing_items ,
381
- "mismatched_items" : mismatched_items ,
382
- }
401
+ result ["cpython_modules" ] = cpymods
402
+ result ["not_implementeds" ] = not_implementeds
383
403
384
- print ()
385
- print ("# out of" , len (cpymods ), "modules:" )
386
- for error_type , modules in result .items ():
387
- print ("# " , error_type , len (modules ))
404
+ print (json .dumps (result ))
388
405
389
406
390
407
def remove_one_indent (s ):
@@ -395,5 +412,54 @@ def remove_one_indent(s):
395
412
compare_src = inspect .getsourcelines (compare )[0 ][1 :]
396
413
output += "" .join (remove_one_indent (line ) for line in compare_src )
397
414
398
- with open ("not_impl.py" , "w" ) as f :
415
+ with open (GENERATED_FILE , "w" ) as f :
399
416
f .write (output + "\n " )
417
+
418
+
419
+ subprocess .run (["cargo" , "build" , "--release" , "--features=ssl" ], check = True )
420
+ result = subprocess .run (
421
+ ["cargo" , "run" , "--release" , "--features=ssl" , "-q" , "--" , GENERATED_FILE ],
422
+ env = {** os .environ .copy (), "RUSTPYTHONPATH" : "Lib" },
423
+ text = True ,
424
+ capture_output = True ,
425
+ )
426
+ # The last line should be json output, the rest of the lines can contain noise
427
+ # because importing certain modules can print stuff to stdout/stderr
428
+ result = json .loads (result .stdout .splitlines ()[- 1 ])
429
+
430
+ if args .json :
431
+ print (json .dumps (result ))
432
+ sys .exit ()
433
+
434
+
435
+ # missing entire modules
436
+ print ("# modules" )
437
+ for modname in result ["not_implemented" ]:
438
+ print (modname , "(entire module)" )
439
+ for modname , exception in result ["failed_to_import" ].items ():
440
+ print (f"{ modname } (exists but not importable: { exception } )" )
441
+
442
+ # missing from builtins
443
+ print ("\n # builtin items" )
444
+ for module , missing_methods in result ["not_implementeds" ].items ():
445
+ for method , reason in missing_methods .items ():
446
+ print (f"{ module } .{ method } " + (f" { reason } " if reason else "" ))
447
+
448
+ # missing from modules
449
+ print ("\n # stdlib items" )
450
+ for modname , missing in result ["missing_items" ].items ():
451
+ for item in missing :
452
+ print (item )
453
+
454
+ if args .signature :
455
+ print ("\n # mismatching signatures (warnings)" )
456
+ for modname , mismatched in result ["mismatched_items" ].items ():
457
+ for (item , rustpy_value , cpython_value ) in mismatched :
458
+ if cpython_value == "ValueError('no signature found')" :
459
+ continue # these items will never match
460
+ print (f"{ item } { rustpy_value } != { cpython_value } " )
461
+
462
+ print ()
463
+ print ("# summary" )
464
+ for error_type , modules in result .items ():
465
+ print ("# " , error_type , len (modules ))
0 commit comments