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
+ 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
54
90
'_testbuffer' , '_testcapi' , '_testimportmultiple' , '_testinternalcapi' , '_testmultiphase' ,
55
91
}
56
92
57
- IGNORED_MODULES = {' this' , ' antigravity' } | PEP_594_MODULES | CPYTHON_SPECIFIC_MODS
93
+ IGNORED_MODULES = {" this" , " antigravity" } | PEP_594_MODULES | CPYTHON_SPECIFIC_MODS
58
94
59
95
sys .path = [
60
96
path
@@ -188,6 +224,7 @@ def onerror(modname):
188
224
def import_module (module_name ):
189
225
import io
190
226
from contextlib import redirect_stdout
227
+
191
228
# Importing modules causes ('Constant String', 2, None, 4) and
192
229
# "Hello world!" to be printed to stdout.
193
230
f = io .StringIO ()
@@ -203,6 +240,7 @@ def import_module(module_name):
203
240
204
241
def is_child (module , item ):
205
242
import inspect
243
+
206
244
item_mod = inspect .getmodule (item )
207
245
return item_mod is module
208
246
@@ -250,7 +288,7 @@ def gen_modules():
250
288
output += gen_methods ()
251
289
output += f"""
252
290
cpymods = { gen_modules ()!r}
253
- libdir = { os .path .abspath ("../ Lib/" ).encode ('utf8' )!r}
291
+ libdir = { os .path .abspath ("Lib/" ).encode ('utf8' )!r}
254
292
255
293
"""
256
294
@@ -260,7 +298,7 @@ def gen_modules():
260
298
extra_info ,
261
299
dir_of_mod_or_error ,
262
300
import_module ,
263
- is_child
301
+ is_child ,
264
302
]
265
303
for fn in REUSED :
266
304
output += "" .join (inspect .getsourcelines (fn )[0 ]) + "\n \n "
@@ -278,7 +316,7 @@ def compare():
278
316
import sys
279
317
import warnings
280
318
from contextlib import redirect_stdout
281
-
319
+ import json
282
320
import platform
283
321
284
322
def method_incompatability_reason (typ , method_name , real_method_value ):
@@ -288,7 +326,7 @@ def method_incompatability_reason(typ, method_name, real_method_value):
288
326
289
327
is_inherited = not attr_is_not_inherited (typ , method_name )
290
328
if is_inherited :
291
- return "inherited"
329
+ return "( inherited) "
292
330
293
331
value = extra_info (getattr (typ , method_name ))
294
332
if value != real_method_value :
@@ -321,16 +359,20 @@ def method_incompatability_reason(typ, method_name, real_method_value):
321
359
322
360
rustpymods = {mod : dir_of_mod_or_error (mod ) for mod in mod_names }
323
361
324
- not_implemented = {}
325
- failed_to_import = {}
326
- missing_items = {}
327
- mismatched_items = {}
362
+ result = {
363
+ "cpython_modules" : {},
364
+ "implemented" : {},
365
+ "not_implemented" : {},
366
+ "failed_to_import" : {},
367
+ "missing_items" : {},
368
+ "mismatched_items" : {},
369
+ }
328
370
for modname , cpymod in cpymods .items ():
329
371
rustpymod = rustpymods .get (modname )
330
372
if rustpymod is None :
331
- not_implemented [modname ] = None
373
+ result [ " not_implemented" ] [modname ] = None
332
374
elif isinstance (rustpymod , Exception ):
333
- failed_to_import [modname ] = rustpymod
375
+ result [ " failed_to_import" ] [modname ] = rustpymod . __class__ . __name__ + str ( rustpymod )
334
376
else :
335
377
implemented_items = sorted (set (cpymod ) & set (rustpymod ))
336
378
mod_missing_items = set (cpymod ) - set (rustpymod )
@@ -343,48 +385,18 @@ def method_incompatability_reason(typ, method_name, real_method_value):
343
385
if rustpymod [item ] != cpymod [item ]
344
386
and not isinstance (cpymod [item ], Exception )
345
387
]
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 } " )
388
+ if mod_missing_items or mod_mismatched_items :
389
+ if mod_missing_items :
390
+ result ["missing_items" ][modname ] = mod_missing_items
391
+ if mod_mismatched_items :
392
+ result ["mismatched_items" ][modname ] = mod_mismatched_items
393
+ else :
394
+ result ["implemented" ][modname ] = None
376
395
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
- }
396
+ result ["cpython_modules" ] = cpymods
397
+ result ["not_implementeds" ] = not_implementeds
383
398
384
- print ()
385
- print ("# out of" , len (cpymods ), "modules:" )
386
- for error_type , modules in result .items ():
387
- print ("# " , error_type , len (modules ))
399
+ print (json .dumps (result ))
388
400
389
401
390
402
def remove_one_indent (s ):
@@ -395,5 +407,54 @@ def remove_one_indent(s):
395
407
compare_src = inspect .getsourcelines (compare )[0 ][1 :]
396
408
output += "" .join (remove_one_indent (line ) for line in compare_src )
397
409
398
- with open ("not_impl.py" , "w" ) as f :
410
+ with open (GENERATED_FILE , "w" ) as f :
399
411
f .write (output + "\n " )
412
+
413
+
414
+ subprocess .run (["cargo" , "build" , "--release" , "--features=ssl" ], check = True )
415
+ result = subprocess .run (
416
+ ["cargo" , "run" , "--release" , "--features=ssl" , "-q" , "--" , GENERATED_FILE ],
417
+ env = {** os .environ .copy (), "RUSTPYTHONPATH" : "Lib" },
418
+ text = True ,
419
+ capture_output = True ,
420
+ )
421
+ # The last line should be json output, the rest of the lines can contain noise
422
+ # because importing certain modules can print stuff to stdout/stderr
423
+ result = json .loads (result .stdout .splitlines ()[- 1 ])
424
+
425
+ if args .json :
426
+ print (json .dumps (result ))
427
+ sys .exit ()
428
+
429
+
430
+ # missing entire modules
431
+ print ("# modules" )
432
+ for modname in result ["not_implemented" ]:
433
+ print (modname , "(entire module)" )
434
+ for modname , exception in result ["failed_to_import" ].items ():
435
+ print (f"{ modname } (exists but not importable: { exception } )" )
436
+
437
+ # missing from builtins
438
+ print ("\n # builtin items" )
439
+ for module , missing_methods in result ["not_implementeds" ].items ():
440
+ for method , reason in missing_methods .items ():
441
+ print (f"{ module } .{ method } " + (f" { reason } " if reason else "" ))
442
+
443
+ # missing from modules
444
+ print ("\n # stdlib items" )
445
+ for modname , missing in result ["missing_items" ].items ():
446
+ for item in missing :
447
+ print (item )
448
+
449
+ if args .signature :
450
+ print ("\n # mismatching signatures (warnings)" )
451
+ for modname , mismatched in result ["mismatched_items" ].items ():
452
+ for (item , rustpy_value , cpython_value ) in mismatched :
453
+ if cpython_value == "ValueError('no signature found')" :
454
+ continue # these items will never match
455
+ print (f"{ item } { rustpy_value } != { cpython_value } " )
456
+
457
+ print ()
458
+ print ("# summary" )
459
+ for error_type , modules in result .items ():
460
+ print ("# " , error_type , len (modules ))
0 commit comments