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
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
+ GENERATED_FILE = "extra_tests/snippets/not_impl.py"
29
+
30
+ implementation = platform .python_implementation ()
31
+ if implementation != "CPython" :
32
+ sys .exit ("whats_left.py must be run under CPython, got {implementation} instead" )
33
+
34
+
35
+ def parse_args ():
36
+ parser = argparse .ArgumentParser (description = "Process some integers." )
37
+ parser .add_argument (
38
+ "--signature" ,
39
+ action = "store_true" ,
40
+ help = "print functions whose signatures don't match CPython's" ,
41
+ )
42
+ parser .add_argument (
43
+ "--json" ,
44
+ action = "store_true" ,
45
+ help = "print output as JSON (instead of line by line)" ,
46
+ )
47
+
48
+ args = parser .parse_args ()
49
+ return args
50
+
51
+
52
+ args = parse_args ()
53
+
18
54
19
55
# modules suggested for deprecation by PEP 594 (www.python.org/dev/peps/pep-0594/)
20
56
# some of these might be implemented, but they are not a priority
57
93
IGNORED_MODULES = {'this' , 'antigravity' } | PEP_594_MODULES | CPYTHON_SPECIFIC_MODS
58
94
59
95
sys .path = [
60
- path
61
- for path in sys .path
62
- if ("site-packages" not in path and "dist-packages" not in path )
96
+ path for path in sys .path if ("site-packages" not in path and "dist-packages" not in path )
63
97
]
64
98
65
99
@@ -250,7 +284,7 @@ def gen_modules():
250
284
output += gen_methods ()
251
285
output += f"""
252
286
cpymods = { gen_modules ()!r}
253
- libdir = { os .path .abspath ("../ Lib/" ).encode ('utf8' )!r}
287
+ libdir = { os .path .abspath ("Lib/" ).encode ('utf8' )!r}
254
288
255
289
"""
256
290
@@ -279,6 +313,8 @@ def compare():
279
313
import warnings
280
314
from contextlib import redirect_stdout
281
315
316
+ import json
317
+ import inspect
282
318
import platform
283
319
284
320
def method_incompatability_reason (typ , method_name , real_method_value ):
@@ -288,7 +324,7 @@ def method_incompatability_reason(typ, method_name, real_method_value):
288
324
289
325
is_inherited = not attr_is_not_inherited (typ , method_name )
290
326
if is_inherited :
291
- return "inherited"
327
+ return "( inherited) "
292
328
293
329
value = extra_info (getattr (typ , method_name ))
294
330
if value != real_method_value :
@@ -321,70 +357,41 @@ def method_incompatability_reason(typ, method_name, real_method_value):
321
357
322
358
rustpymods = {mod : dir_of_mod_or_error (mod ) for mod in mod_names }
323
359
324
- not_implemented = {}
325
- failed_to_import = {}
326
- missing_items = {}
327
- mismatched_items = {}
360
+ result = {
361
+ "cpython_modules" : {},
362
+ "implemented" : {},
363
+ "not_implemented" : {},
364
+ "failed_to_import" : {},
365
+ "missing_items" : {},
366
+ "mismatched_items" : {},
367
+ }
328
368
for modname , cpymod in cpymods .items ():
329
369
rustpymod = rustpymods .get (modname )
330
370
if rustpymod is None :
331
- not_implemented [modname ] = None
371
+ result [ " not_implemented" ] [modname ] = None
332
372
elif isinstance (rustpymod , Exception ):
333
- failed_to_import [modname ] = rustpymod
373
+ result [ " failed_to_import" ] [modname ] = rustpymod . __class__ . __name__ + str ( rustpymod )
334
374
else :
335
375
implemented_items = sorted (set (cpymod ) & set (rustpymod ))
336
376
mod_missing_items = set (cpymod ) - set (rustpymod )
337
- mod_missing_items = sorted (
338
- f"{ modname } .{ item } " for item in mod_missing_items
339
- )
377
+ mod_missing_items = sorted (f"{ modname } .{ item } " for item in mod_missing_items )
340
378
mod_mismatched_items = [
341
379
(f"{ modname } .{ item } " , rustpymod [item ], cpymod [item ])
342
380
for item in implemented_items
343
- if rustpymod [item ] != cpymod [item ]
344
- and not isinstance (cpymod [item ], Exception )
381
+ if rustpymod [item ] != cpymod [item ] and not isinstance (cpymod [item ], Exception )
345
382
]
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 )
383
+ if mod_missing_items or mod_mismatched_items :
384
+ if mod_missing_items :
385
+ result ["missing_items" ][modname ] = mod_missing_items
386
+ if mod_mismatched_items :
387
+ result ["mismatched_items" ][modname ] = mod_mismatched_items
388
+ else :
389
+ result ["implemented" ][modname ] = None
369
390
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 } " )
376
-
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
- }
391
+ result ["cpython_modules" ] = cpymods
392
+ result ["not_implementeds" ] = not_implementeds
383
393
384
- print ()
385
- print ("# out of" , len (cpymods ), "modules:" )
386
- for error_type , modules in result .items ():
387
- print ("# " , error_type , len (modules ))
394
+ print (json .dumps (result ))
388
395
389
396
390
397
def remove_one_indent (s ):
@@ -395,5 +402,52 @@ def remove_one_indent(s):
395
402
compare_src = inspect .getsourcelines (compare )[0 ][1 :]
396
403
output += "" .join (remove_one_indent (line ) for line in compare_src )
397
404
398
- with open ("not_impl.py" , "w" ) as f :
405
+ with open (GENERATED_FILE , "w" ) as f :
399
406
f .write (output + "\n " )
407
+
408
+
409
+ subprocess .run (["cargo" , "build" , "--release" , "--features=ssl" ], check = True )
410
+ result = subprocess .run (
411
+ ["cargo" , "run" , "--release" , "--features=ssl" , "-q" , "--" , GENERATED_FILE ],
412
+ env = {** os .environ .copy (), "RUSTPYTHONPATH" : "Lib" },
413
+ text = True ,
414
+ capture_output = True ,
415
+ )
416
+ # The last line should be json output, the rest of the lines can contain noise
417
+ # because importing certain modules can print stuff to stdout/stderr
418
+ result = json .loads (result .stdout .splitlines ()[- 1 ])
419
+
420
+ if args .json :
421
+ print (json .dumps (result ))
422
+ sys .exit ()
423
+
424
+
425
+ # missing entire modules
426
+ print ('# modules' )
427
+ for modname in result ["not_implemented" ]:
428
+ print (modname , "(entire module)" )
429
+ for modname , exception in result ["failed_to_import" ].items ():
430
+ print (f"{ modname } (exists but not importable: { exception } )" )
431
+
432
+ # missing from builtins
433
+ print ("\n # builtin items" )
434
+ for module , missing_methods in result ["not_implementeds" ].items ():
435
+ for method , reason in missing_methods .items ():
436
+ print (f"{ module } .{ method } " + (f" { reason } " if reason else "" ))
437
+
438
+ # missing from modules
439
+ print ("\n # stdlib items" )
440
+ for modname , missing in result ["missing_items" ].items ():
441
+ for item in missing :
442
+ print (item )
443
+
444
+ if args .signature :
445
+ print ("\n # mismatching signatures (warnings)" )
446
+ for modname , mismatched in result ["mismatched_items" ].items ():
447
+ for (item , rustpy_value , cpython_value ) in mismatched :
448
+ print (f"{ item } { rustpy_value } != { cpython_value } " )
449
+
450
+ print ()
451
+ print ("# summary" )
452
+ for error_type , modules in result .items ():
453
+ print ("# " , error_type , len (modules ))
0 commit comments