Explorar el Código

use cpython style `next()`

blueloveTH hace 1 año
padre
commit
6189aaca94

+ 10 - 37
docs/features/differences.md

@@ -30,40 +30,13 @@ The easiest way to test a feature is to [try it on your browser](https://pocketp
 ## Different behaviors
 
 1. positional and keyword arguments are strictly evaluated.
-2. When a generator is exhausted, `StopIteration` is returned instead of raised.
-3. `++i` and `--j` is an increment/decrement statement, not an expression.
-4. `int` does not derive from `bool`.
-5. `int` is 64-bit. You can use `long` type explicitly for arbitrary sized integers.
-6. `__ne__` is not required. Define `__eq__` is enough.
-7. Raw string cannot have boundary quotes in it, even escaped. See [#55](https://github.com/pocketpy/pocketpy/issues/55).
-8. In a starred unpacked assignment, e.g. `a, b, *c = x`, the starred variable can only be presented in the last position. `a, *b, c = x` is not supported.
-9. A `Tab` is equivalent to 4 spaces. You can mix `Tab` and spaces in indentation, but it is not recommended.
-10. `%`, `&`, `//`, `^` and `|` for `int` behave the same as C, not python.
-11. `str.split` and `str.splitlines` will remove all empty entries.
-12. `__getattr__`, `__setattr__` and `__delattr__` can only be set in cpp.
-
-### Make `next()` compatible with cpython
-
-You can use the following code to make `next()` compatible with cpython temporarily.
-
-```python
-import builtins
-
-def next(obj):
-    res = builtins.next(obj)
-    if res is StopIteration:
-        raise StopIteration
-    return res
-    
-a = iter([1])
-assert next(a) == 1
-
-try:
-    next(a)
-except StopIteration:
-    print("a is exhausted")
-```
-
-!!!
-Do not change `builtins.next`. It will break internal functions which rely on `StopIteration` as return value.
-!!!
+2. `++i` and `--j` is an increment/decrement statement, not an expression.
+3. `int` does not derive from `bool`.
+4. `int` is 64-bit. You can use `long` type explicitly for arbitrary sized integers.
+5. `__ne__` is not required. Define `__eq__` is enough.
+6. Raw string cannot have boundary quotes in it, even escaped. See [#55](https://github.com/pocketpy/pocketpy/issues/55).
+7. In a starred unpacked assignment, e.g. `a, b, *c = x`, the starred variable can only be presented in the last position. `a, *b, c = x` is not supported.
+8. A `Tab` is equivalent to 4 spaces. You can mix `Tab` and spaces in indentation, but it is not recommended.
+9. `%`, `&`, `//`, `^` and `|` for `int` behave the same as C, not python.
+10. `str.split` and `str.splitlines` will remove all empty entries.
+11. `__getattr__`, `__setattr__` and `__delattr__` can only be set in cpp.

+ 1 - 1
docs/license.md

@@ -11,7 +11,7 @@ pkpy is licensed under the [MIT License](http://opensource.org/licenses/MIT).
 ```
 MIT License
 
-Copyright (c) 2023 blueloveTH
+Copyright (c) 2024 blueloveTH
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal

+ 1 - 0
include/pocketpy/modules.h

@@ -14,5 +14,6 @@ void add_module_dis(VM* vm);
 void add_module_gc(VM* vm);
 void add_module_line_profiler(VM* vm);
 void add_module_enum(VM* vm);
+void add_module___builtins(VM* vm);
 
 }   // namespace pkpy

+ 7 - 0
include/typings/__builtins.pyi

@@ -0,0 +1,7 @@
+from typing import Any
+
+def next(iter) -> Any | StopIteration:
+    ...
+
+def _enable_instance_dict(obj) -> None:
+    ...

+ 5 - 0
pyrightconfig.json

@@ -0,0 +1,5 @@
+{
+    "stubPath": "include/typings",
+    "reportMissingModuleSource": "none",
+    "pythonVersion": "3.10"
+}

+ 17 - 17
python/builtins.py

@@ -1,36 +1,36 @@
-import sys as _sys
-import operator as _operator
+from operator import lt as __operator_lt
+from operator import gt as __operator_gt
+from __builtins import next as __builtins_next
 
-def print(*args, sep=' ', end='\n'):
-    s = sep.join([str(i) for i in args])
-    _sys.stdout.write(s + end)
-
-def _minmax_reduce(op, args, key):
-    if key is None: 
+def __minmax_reduce(op, args, key):
+    if key is None:
         if len(args) == 2:
             return args[0] if op(args[0], args[1]) else args[1]
-        key = lambda x: x
     if len(args) == 0:
         raise TypeError('expected 1 arguments, got 0')
     if len(args) == 1:
         args = args[0]
     args = iter(args)
-    res = next(args)
+    res = __builtins_next(args)
     if res is StopIteration:
         raise ValueError('args is an empty sequence')
     while True:
-        i = next(args)
+        i = __builtins_next(args)
         if i is StopIteration:
             break
-        if op(key(i), key(res)):
-            res = i
+        if key is None:
+            if op(i, res):
+                res = i
+        else:
+            if op(key(i), key(res)):
+                res = i
     return res
 
 def min(*args, key=None):
-    return _minmax_reduce(_operator.lt, args, key)
+    return __minmax_reduce(__operator_lt, args, key)
 
 def max(*args, key=None):
-    return _minmax_reduce(_operator.gt, args, key)
+    return __minmax_reduce(__operator_gt, args, key)
 
 def all(iterable):
     for i in iterable:
@@ -69,8 +69,8 @@ def zip(a, b):
     a = iter(a)
     b = iter(b)
     while True:
-        ai = next(a)
-        bi = next(b)
+        ai = __builtins_next(a)
+        bi = __builtins_next(b)
         if ai is StopIteration or bi is StopIteration:
             break
         yield ai, bi

+ 3 - 1
python/collections.py

@@ -7,10 +7,12 @@ def Counter(iterable):
             a[x] = 1
     return a
 
+from __builtins import _enable_instance_dict
+
 class defaultdict(dict):
     def __init__(self, default_factory, *args):
         super().__init__(*args)
-        self._enable_instance_dict()
+        _enable_instance_dict(self)
         self.default_factory = default_factory
 
     def __missing__(self, key):

+ 2 - 0
python/functools.py

@@ -1,3 +1,5 @@
+from __builtins import next
+
 class cache:
     def __init__(self, f):
         self.f = f

+ 2 - 0
python/itertools.py

@@ -1,3 +1,5 @@
+from __builtins import next
+
 def zip_longest(a, b):
     a = iter(a)
     b = iter(b)

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
src/_generated.cpp


+ 16 - 0
src/modules.cpp

@@ -337,4 +337,20 @@ void add_module_enum(VM* vm){
         };
 }
 
+void add_module___builtins(VM* vm){
+    PyObject* mod = vm->new_module("__builtins");
+
+    vm->bind_func<1>(mod, "next", [](VM* vm, ArgsView args){
+        return vm->py_next(args[0]);
+    });
+
+    vm->bind_func<1>(mod, "_enable_instance_dict", [](VM* vm, ArgsView args){
+        PyObject* self = args[0];
+        if(is_tagged(self)) vm->TypeError("object: tagged object cannot enable instance dict");
+        if(self->is_attr_valid()) vm->RuntimeError("object: instance dict is already enabled");
+        self->_enable_instance_dict();
+        return vm->None;
+    });
+}
+
 }   // namespace pkpy

+ 19 - 14
src/pocketpy.cpp

@@ -297,7 +297,9 @@ void init_builtins(VM* _vm) {
     });
 
     _vm->bind_func<1>(_vm->builtins, "next", [](VM* vm, ArgsView args) {
-        return vm->py_next(args[0]);
+        PyObject* retval = vm->py_next(args[0]);
+        if(retval == vm->StopIteration) vm->_error(vm->call(vm->StopIteration));
+        return retval;
     });
 
     _vm->bind_func<1>(_vm->builtins, "bin", [](VM* vm, ArgsView args) {
@@ -350,18 +352,6 @@ void init_builtins(VM* _vm) {
         return vm->heap.gcnew<DummyInstance>(t);
     });
 
-    _vm->bind_method<0>(VM::tp_object, "_enable_instance_dict", [](VM* vm, ArgsView args){
-        PyObject* self = args[0];
-        if(is_tagged(self)){
-            vm->TypeError("object: tagged object cannot enable instance dict");
-        }
-        if(self->is_attr_valid()){
-            vm->TypeError("object: instance dict is already enabled");
-        }
-        self->_enable_instance_dict();
-        return vm->None;
-    });
-
     // tp_type
     _vm->bind_constructor<2>(_vm->_t(VM::tp_type), PK_LAMBDA(vm->_t(args[1])));
 
@@ -1588,6 +1578,21 @@ void VM::post_init(){
         return VAR(MappingProxy(args[0]));
     });
 
+    bind(builtins, "print(*args, sep=' ', end='\\n')", [](VM* vm, ArgsView args) {
+        const Tuple& _0 = CAST(Tuple&, args[0]);
+        const Str& _1 = CAST(Str&, args[1]);
+        const Str& _2 = CAST(Str&, args[2]);
+        SStream ss;
+        for(int i=0; i<_0.size(); i++){
+            ss << CAST(Str&, vm->py_str(_0[i]));
+            if(i != _0.size()-1) ss << _1;
+        }
+        ss << _2;
+        vm->stdout_write(ss.str());
+        return vm->None;
+    });
+
+    add_module___builtins(vm);
     add_module_sys(this);
     add_module_traceback(this);
     add_module_time(this);
@@ -1599,7 +1604,7 @@ void VM::post_init(){
     add_module_random(this);
     add_module_base64(this);
     add_module_operator(this);
-
+    
     _lazy_modules["this"] = kPythonLibs_this;
     _lazy_modules["functools"] = kPythonLibs_functools;
     _lazy_modules["heapq"] = kPythonLibs_heapq;

+ 4 - 7
tests/28_iter.py

@@ -1,3 +1,5 @@
+from __builtins import next
+
 a = [1, 2, 3]
 a = iter(a)
 
@@ -37,14 +39,9 @@ assert next(i) == 5
 assert next(i) == StopIteration
 assert next(i) == StopIteration
 
-import builtins
+# test normal next
+from builtins import next
 
-def next(obj):
-    res = builtins.next(obj)
-    if res is StopIteration:
-        raise StopIteration
-    return res
-    
 a = iter([1])
 assert next(a) == 1
 

+ 5 - 1
tests/45_yield.py

@@ -64,7 +64,11 @@ try:
 except ValueError:
     pass
 
-assert next(t) == StopIteration
+try:
+    next(t)
+    exit(1)
+except StopIteration:
+    pass
 
 def f():
     yield 1

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio