├── stdlib ├── glob │ ├── testdata │ │ ├── test_golden.txt │ │ └── test.py │ ├── glob_test.go │ └── glob.go ├── time │ ├── testdata │ │ ├── test_golden.txt │ │ └── test.py │ └── time_test.go ├── binascii │ ├── testdata │ │ └── test_golden.txt │ └── binascii_test.go ├── math │ ├── math_test.go │ └── tests │ │ ├── libulp.py │ │ └── libtest.py ├── builtin │ ├── builtin_test.go │ └── tests │ │ ├── lib.py │ │ └── libtest.py ├── os │ ├── os_test.go │ └── testdata │ │ └── test_golden.txt ├── array │ └── array_test.go ├── string │ ├── string_test.go │ ├── testdata │ │ ├── test_golden.txt │ │ └── test.py │ └── string.go └── tempfile │ ├── tempfile_test.go │ └── testdata │ ├── test_golden.txt │ └── test.py ├── testdata ├── hello_golden.txt └── hello.py ├── pytest ├── testdata │ ├── hello_golden.txt │ ├── hello.py │ └── tests │ │ ├── libtest.py │ │ └── module.py └── pytest_test.go ├── repl ├── web │ ├── .gitignore │ ├── Makefile │ ├── serve.go │ ├── README.md │ ├── wasm_exec.js.patch │ ├── loader.js │ ├── index.html │ └── main.go ├── repl_test.go └── cli │ └── cli.go ├── .gitignore ├── symtable ├── readsymtab_module │ ├── build.sh │ ├── setup.py │ └── readsymtab.c └── stringer.go ├── .gometalinter.json ├── version.go ├── vm ├── tests │ ├── raise2.py │ ├── raise1.py │ ├── import_name.py │ ├── import_from.py │ ├── lib.py │ ├── README.md │ ├── lib1.py │ ├── import_star.py │ ├── attr.py │ ├── comprehensions.py │ ├── class.py │ ├── call.py │ ├── builtin.py │ ├── loops.py │ ├── libtest.py │ ├── lists.py │ ├── generators.py │ ├── ops.py │ ├── with.py │ └── exceptions.py ├── benchmarks │ ├── fib.py │ └── fibtc.py ├── vm.go ├── vm_test.go └── builtin.go ├── RELEASE.md ├── py ├── py_test.go ├── tests │ ├── staticmethod.py │ ├── zip.py │ ├── classmethod.py │ ├── complex.py │ ├── internal.py │ ├── slice.py │ ├── iter.py │ ├── libtest.py │ ├── file.py │ ├── float.py │ ├── tuple.py │ ├── map.py │ ├── lib_gen_int_tests.py │ ├── filter.py │ ├── set.py │ ├── dict.py │ ├── function.py │ ├── range.py │ ├── bytes.py │ └── list.py ├── range_repr19.go ├── range_repr110.go ├── cell.go ├── ellipsis.go ├── call_iterator.go ├── boundmethod.go ├── property.go ├── type_test.go ├── none.go ├── iterator.go ├── map.go ├── object.go ├── zip.go ├── filter.go ├── staticmethod.go ├── enumerate.go ├── classmethod.go ├── bool.go ├── traceback.go └── string_test.go ├── parser ├── parser.go ├── testparser │ ├── py3compile.py │ ├── test.sh │ └── testparser.go ├── grammar_test.go └── stringescape.go ├── examples ├── embedding │ ├── main_test.go │ ├── lib │ │ ├── REPL-startup.py │ │ └── mylib.py │ ├── testdata │ │ ├── mylib-demo_golden.txt │ │ └── mylib-demo.py │ └── main.go ├── chudtest.txt ├── pi_chudnovsky_bs.py └── multi-context │ └── main.go ├── go.mod ├── ast ├── asttest.py ├── dump_test.go ├── dump.go └── walk_test.go ├── bin └── install-python.sh ├── .goreleaser.yml ├── compile ├── diffdis.py ├── instructions_test.go └── legacy.go ├── LICENSE ├── go.sum ├── main_test.go ├── main.go ├── ci └── run-tests.go ├── py3test.py ├── .github └── workflows │ └── ci.yml └── README.md /stdlib/glob/testdata/test_golden.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testdata/hello_golden.txt: -------------------------------------------------------------------------------- 1 | hello, world! 2 | -------------------------------------------------------------------------------- /testdata/hello.py: -------------------------------------------------------------------------------- 1 | print("hello, world!") 2 | -------------------------------------------------------------------------------- /pytest/testdata/hello_golden.txt: -------------------------------------------------------------------------------- 1 | hello 2 | world 3 | bye. 4 | -------------------------------------------------------------------------------- /repl/web/.gitignore: -------------------------------------------------------------------------------- 1 | gpython.wasm 2 | gpython.js 3 | gpython.js.map 4 | -------------------------------------------------------------------------------- /stdlib/time/testdata/test_golden.txt: -------------------------------------------------------------------------------- 1 | # sleep 2 | caught error: ValueError: 'sleep length must be non-negative' 3 | caught error: TypeError: 'sleep() argument 1 must be float, not str' 4 | OK 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .#* 3 | \#*\# 4 | gpython 5 | hello.* 6 | primes.* 7 | __pycache__ 8 | cover.out 9 | /junk 10 | /dist 11 | 12 | # tests 13 | stdlib/builtin/testfile 14 | examples/embedding/embedding 15 | -------------------------------------------------------------------------------- /symtable/readsymtab_module/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | rm -rf build 4 | PYTHON=/opt/python3.4/bin/python3.4 5 | INCLUDE=/opt/python3.4/include/python3.4m 6 | $PYTHON setup.py build_ext --inplace 7 | cp -av *.so *.dll .. 8 | -------------------------------------------------------------------------------- /pytest/testdata/hello.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | print("hello") 6 | print("world") 7 | print("bye.") 8 | -------------------------------------------------------------------------------- /.gometalinter.json: -------------------------------------------------------------------------------- 1 | { 2 | "Enable": [ 3 | "deadcode", 4 | "errcheck", 5 | "goimports", 6 | "ineffassign", 7 | "structcheck", 8 | "varcheck", 9 | "vet" 10 | ], 11 | "EnableGC": true, 12 | "Vendor": true 13 | } 14 | -------------------------------------------------------------------------------- /repl/web/Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | GOARCH=wasm GOOS=js go build -o gpython.wasm 3 | gopherjs build -m -o gpython.js 4 | 5 | serve: build 6 | go run serve.go 7 | 8 | upload: build 9 | rclone -P copy --include="*.{wasm,js,css,html}" . gpythonweb: 10 | @echo See https://gpython.org/ 11 | -------------------------------------------------------------------------------- /pytest/testdata/tests/libtest.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | """ 6 | Simple test harness 7 | """ 8 | 9 | def testFunc(): 10 | return 11 | 12 | -------------------------------------------------------------------------------- /version.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | var ( 8 | version = "dev" 9 | commit = "none" 10 | date = "unknown" 11 | ) 12 | -------------------------------------------------------------------------------- /vm/tests/raise2.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | doc="Exception not in block" 6 | err=ZeroDivisionError() 7 | 1/0 8 | 9 | err=None 10 | doc="finished" 11 | -------------------------------------------------------------------------------- /vm/tests/raise1.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | doc="Raise not in block" 6 | err=ValueError("potato") 7 | raise err 8 | 9 | err=None 10 | doc="finished" 11 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Making a release # 2 | 3 | Compile and test 4 | 5 | Then run 6 | 7 | goreleaser --rm-dist --snapshot 8 | 9 | To test the build 10 | 11 | When happy, tag the release 12 | 13 | git tag -s v0.0.XX -m "Release v0.0.XX" 14 | 15 | Then do a release build (set GITHUB token first) 16 | 17 | goreleaser --rm-dist 18 | -------------------------------------------------------------------------------- /pytest/testdata/tests/module.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | from libtest import testFunc 6 | 7 | doc="module" 8 | assert True 9 | assert not False 10 | assert testFunc() is None 11 | 12 | doc="finished" 13 | -------------------------------------------------------------------------------- /stdlib/binascii/testdata/test_golden.txt: -------------------------------------------------------------------------------- 1 | globals: 2 | 3 | binascii.Error: 4 | 5 | 6 | binascii.Incomplete: 7 | 8 | expected an exception: binascii.b2a_base64() argument 1 must be bytes-like, not str 9 | expected an exception: Odd-length string 10 | expected an exception: Non-hexadecimal digit found 11 | done. 12 | -------------------------------------------------------------------------------- /vm/tests/import_name.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | doc="IMPORT_NAME" 6 | 7 | import lib 8 | 9 | assert lib.libfn() == 42 10 | assert lib.libvar == 43 11 | assert lib.libclass().method() == 44 12 | 13 | doc="finished" 14 | -------------------------------------------------------------------------------- /py/py_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package py_test 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/go-python/gpython/pytest" 11 | ) 12 | 13 | func TestPy(t *testing.T) { 14 | pytest.RunTests(t, "tests") 15 | } 16 | -------------------------------------------------------------------------------- /vm/tests/import_from.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | doc="IMPORT_FROM" 6 | 7 | from lib import libfn, libvar, libclass 8 | 9 | assert libfn() == 42 10 | assert libvar == 43 11 | assert libclass().method() == 44 12 | 13 | doc="finished" 14 | -------------------------------------------------------------------------------- /vm/tests/lib.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | # Some targets to be imported 6 | 7 | def libfn(): 8 | return 42 9 | 10 | libvar = 43 11 | 12 | class libclass: 13 | def method(self): 14 | return 44 15 | 16 | _libprivate = 45 17 | -------------------------------------------------------------------------------- /stdlib/math/math_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package math_test 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/go-python/gpython/pytest" 11 | ) 12 | 13 | func TestVm(t *testing.T) { 14 | pytest.RunTests(t, "tests") 15 | } 16 | -------------------------------------------------------------------------------- /stdlib/builtin/builtin_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package builtin_test 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/go-python/gpython/pytest" 11 | ) 12 | 13 | func TestVm(t *testing.T) { 14 | pytest.RunTests(t, "tests") 15 | } 16 | -------------------------------------------------------------------------------- /stdlib/os/os_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package os_test 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/go-python/gpython/pytest" 11 | ) 12 | 13 | func TestOs(t *testing.T) { 14 | pytest.RunScript(t, "./testdata/test.py") 15 | } 16 | -------------------------------------------------------------------------------- /stdlib/builtin/tests/lib.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | # Some targets to be imported 6 | 7 | def libfn(): 8 | return 42 9 | 10 | libvar = 43 11 | 12 | class libclass: 13 | def method(self): 14 | return 44 15 | 16 | _libprivate = 45 17 | -------------------------------------------------------------------------------- /stdlib/glob/glob_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package glob_test 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/go-python/gpython/pytest" 11 | ) 12 | 13 | func TestGlob(t *testing.T) { 14 | pytest.RunScript(t, "./testdata/test.py") 15 | } 16 | -------------------------------------------------------------------------------- /stdlib/time/time_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package time_test 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/go-python/gpython/pytest" 11 | ) 12 | 13 | func TestTime(t *testing.T) { 14 | pytest.RunScript(t, "./testdata/test.py") 15 | } 16 | -------------------------------------------------------------------------------- /stdlib/array/array_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package array_test 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/go-python/gpython/pytest" 11 | ) 12 | 13 | func TestArray(t *testing.T) { 14 | pytest.RunScript(t, "./testdata/test.py") 15 | } 16 | -------------------------------------------------------------------------------- /stdlib/string/string_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package string_test 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/go-python/gpython/pytest" 11 | ) 12 | 13 | func TestString(t *testing.T) { 14 | pytest.RunScript(t, "./testdata/test.py") 15 | } 16 | -------------------------------------------------------------------------------- /py/tests/staticmethod.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | doc="staticmethod" 6 | 7 | class A: 8 | @staticmethod 9 | def fn(p): 10 | return p+1 11 | 12 | a = A() 13 | assert a.fn(1) == 2 14 | 15 | a.x = 3 16 | assert a.x == 3 17 | 18 | doc="finished" 19 | -------------------------------------------------------------------------------- /py/tests/zip.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | doc="zip" 6 | a = [1, 2, 3, 4, 5] 7 | b = [10, 11, 12] 8 | c = [e for e in zip(a, b)] 9 | assert len(c) == 3 10 | for idx, e in enumerate(c): 11 | assert a[idx] == c[idx][0] 12 | assert b[idx] == c[idx][1] 13 | doc="finished" -------------------------------------------------------------------------------- /parser/parser.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // This file holds the go generate command to run yacc on the grammar in grammar.y. 6 | // To build y.go: 7 | // % go generate 8 | // % go build 9 | 10 | //go:generate goyacc -v y.output grammar.y 11 | package parser 12 | -------------------------------------------------------------------------------- /stdlib/binascii/binascii_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package binascii_test 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/go-python/gpython/pytest" 11 | ) 12 | 13 | func TestBinascii(t *testing.T) { 14 | pytest.RunScript(t, "./testdata/test.py") 15 | } 16 | -------------------------------------------------------------------------------- /stdlib/tempfile/tempfile_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package tempfile_test 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/go-python/gpython/pytest" 11 | ) 12 | 13 | func TestTempfile(t *testing.T) { 14 | pytest.RunScript(t, "./testdata/test.py") 15 | } 16 | -------------------------------------------------------------------------------- /examples/embedding/main_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/go-python/gpython/pytest" 11 | ) 12 | 13 | func TestEmbeddedExample(t *testing.T) { 14 | pytest.RunScript(t, "./testdata/mylib-demo.py") 15 | } 16 | -------------------------------------------------------------------------------- /py/tests/classmethod.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | doc="classmethod" 6 | 7 | class A: 8 | @classmethod 9 | def fn(cls, p): 10 | assert cls is A 11 | return p+1 12 | 13 | a = A() 14 | assert a.fn(1) == 2 15 | 16 | a.x = 3 17 | assert a.x == 3 18 | 19 | doc="finished" 20 | -------------------------------------------------------------------------------- /vm/tests/README.md: -------------------------------------------------------------------------------- 1 | Tests for VM 2 | ============ 3 | 4 | These simple programs are designed to exercise the VM. 5 | 6 | They should run with no errors raised. 7 | 8 | They should also all run clean with python3.4 9 | 10 | Set doc="string" before every test 11 | 12 | Set doc="finished" at the end 13 | 14 | If you want to test an error is raised properly, then set 15 | err=ErrorObject so the test runner can check the error was raised 16 | properly. 17 | -------------------------------------------------------------------------------- /vm/benchmarks/fib.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | # Benchmark adapted from https://github.com/d5/tengobench/ 6 | doc="fib recursion test" 7 | def fib(n): 8 | if n == 0: 9 | return 0 10 | elif n == 1: 11 | return 1 12 | return fib(n - 2) + fib(n - 1) 13 | 14 | fib(25) 15 | doc="finished" 16 | -------------------------------------------------------------------------------- /vm/tests/lib1.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | # Some targets to be imported 6 | 7 | __all__ = [ 8 | "lib1fn", 9 | "lib1var", 10 | ] 11 | 12 | def lib1fn(): 13 | return 42 14 | 15 | lib1var = 43 16 | 17 | class lib1class: 18 | def method(self): 19 | return 44 20 | 21 | _lib1private = 45 22 | -------------------------------------------------------------------------------- /examples/embedding/lib/REPL-startup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | # This file is called from main.go when in REPL mode 6 | 7 | # This is here to demonstrate making life easier for your users in REPL mode 8 | # by doing pre-setup here so they don't have to import every time they start. 9 | from mylib import * 10 | 11 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/go-python/gpython 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/google/go-cmp v0.5.8 7 | github.com/gopherjs/gopherwasm v1.1.0 8 | github.com/peterh/liner v1.2.2 9 | ) 10 | 11 | require ( 12 | github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c // indirect 13 | github.com/mattn/go-runewidth v0.0.13 // indirect 14 | github.com/rivo/uniseg v0.3.4 // indirect 15 | golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2 // indirect 16 | ) 17 | -------------------------------------------------------------------------------- /vm/benchmarks/fibtc.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | # Benchmark adapted from https://github.com/d5/tengobench/ 6 | doc="fib tail call recursion test" 7 | def fib(n, a, b): 8 | if n == 0: 9 | return a 10 | elif n == 1: 11 | return b 12 | return fib(n-1, b, a+b) 13 | 14 | fib(35, 0, 1) 15 | doc="finished" 16 | -------------------------------------------------------------------------------- /repl/web/serve.go: -------------------------------------------------------------------------------- 1 | //go:build none 2 | // +build none 3 | 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "log" 9 | "mime" 10 | "net/http" 11 | ) 12 | 13 | func main() { 14 | mime.AddExtensionType(".wasm", "application/wasm") 15 | mime.AddExtensionType(".js", "application/javascript") 16 | mux := http.NewServeMux() 17 | mux.Handle("/", http.FileServer(http.Dir("."))) 18 | fmt.Printf("Serving on http://localhost:3000/\n") 19 | log.Fatal(http.ListenAndServe(":3000", mux)) 20 | } 21 | -------------------------------------------------------------------------------- /symtable/readsymtab_module/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | from distutils.core import setup, Extension 6 | 7 | readsymtab = Extension('readsymtab', sources = ['readsymtab.c']) 8 | 9 | setup (name = 'readsymtab', 10 | version = '1.0', 11 | description = 'Read the symbol table', 12 | ext_modules = [readsymtab] 13 | ) 14 | -------------------------------------------------------------------------------- /stdlib/builtin/tests/libtest.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | """ 6 | Simple test harness 7 | """ 8 | 9 | def assertRaises(expecting, fn, *args, **kwargs): 10 | """Check the exception was raised - don't check the text""" 11 | try: 12 | fn(*args, **kwargs) 13 | except expecting as e: 14 | pass 15 | else: 16 | assert False, "%s not raised" % (expecting,) 17 | 18 | -------------------------------------------------------------------------------- /parser/testparser/py3compile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2018 The go-python Authors. All rights reserved. 4 | # Use of this source code is governed by a BSD-style 5 | # license that can be found in the LICENSE file. 6 | 7 | """ 8 | Compile any files passed in on the command line 9 | """ 10 | 11 | import sys 12 | for path in sys.argv[1:]: 13 | print("Compiling %s" % path) 14 | with open(path) as f: 15 | try: 16 | data = f.read() 17 | compile(data, path, "exec") 18 | except Exception as e: 19 | print("Failed: %s" % e) 20 | -------------------------------------------------------------------------------- /ast/asttest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2018 The go-python Authors. All rights reserved. 4 | # Use of this source code is governed by a BSD-style 5 | # license that can be found in the LICENSE file. 6 | 7 | """ 8 | Do some ast stuff 9 | """ 10 | 11 | import sys 12 | import ast 13 | 14 | def dump(path): 15 | print(path) 16 | a = ast.parse(open(path).read(), path) 17 | print(ast.dump(a, annotate_fields=True, include_attributes=False)) 18 | 19 | def main(): 20 | for path in sys.argv[1:]: 21 | dump(path) 22 | 23 | if __name__ == "__main__": 24 | main() 25 | -------------------------------------------------------------------------------- /parser/testparser/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Run testparser over python3 source 3 | # 4 | # Pass in args to be passed to testparser, eg -c, -l 5 | # Parses by default 6 | 7 | PY3SOURCE=~/Code/cpython 8 | 9 | go install 10 | 11 | # Grep out python2 source code which we can't parse and files with deliberate syntax errors 12 | find $PY3SOURCE -type f -name \*.py | egrep -v "Lib/(lib2to3/tests|test/bad.*py)|Tools/(hg|msi|test2to3)/" | xargs testparser "$@" 13 | 14 | #find $PY3SOURCE -type f -name \*.py | egrep -v "Lib/(lib2to3/tests|test/bad.*py)|Tools/(hg|msi|test2to3)/" | xargs ./py3compile.py "$@" 15 | 16 | -------------------------------------------------------------------------------- /stdlib/tempfile/testdata/test_golden.txt: -------------------------------------------------------------------------------- 1 | test tempfile 2 | tempfile.tempdir is None [OK] 3 | mkdtemp() [OK] 4 | mkdtemp(prefix='prefix-', suffix='-suffix') [OK] 5 | mkdtemp(prefix='prefix-', suffix='-suffix', dir=top) [OK] 6 | mkdtemp(prefix='prefix-', suffix='-suffix', dir=top) [OK] 7 | caught: TypeError: "Can't mix bytes and non-bytes in path components" [OK] 8 | mkstemp() [OK] 9 | mkstemp(prefix='prefix-', suffix='-suffix') [OK] 10 | mkstemp(prefix='prefix-', suffix='-suffix', dir=top) [OK] 11 | mkstemp(prefix='prefix-', suffix='-suffix', dir=top) [OK] 12 | caught: TypeError: "Can't mix bytes and non-bytes in path components" [OK] 13 | OK 14 | -------------------------------------------------------------------------------- /repl/web/README.md: -------------------------------------------------------------------------------- 1 | # Gpython Web 2 | 3 | This implements a web viewable version of the gpython REPL. 4 | 5 | This is done by compiling gpython into wasm and running that in the 6 | browser. 7 | 8 | [Try it online.](https://www.craig-wood.com/nick/gpython/) 9 | 10 | ## Build and run 11 | 12 | `make build` will build with go wasm (you'll need go1.11 minimum) 13 | 14 | `make serve` will run a local webserver you can see the results on 15 | 16 | ## Thanks 17 | 18 | Thanks to [jQuery Terminal](https://terminal.jcubic.pl/) for the 19 | terminal emulator and the go team for great [wasm 20 | support](https://github.com/golang/go/wiki/WebAssembly). 21 | -------------------------------------------------------------------------------- /stdlib/string/testdata/test_golden.txt: -------------------------------------------------------------------------------- 1 | globals: 2 | 3 | string.whitespace: 4 | ' \t\n\r\x0b\x0c' 5 | 6 | string.ascii_lowercase: 7 | 'abcdefghijklmnopqrstuvwxyz' 8 | 9 | string.ascii_uppercase: 10 | 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 11 | 12 | string.ascii_letters: 13 | 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 14 | 15 | string.digits: 16 | '0123456789' 17 | 18 | string.hexdigits: 19 | '0123456789abcdefABCDEF' 20 | 21 | string.octdigits: 22 | '01234567' 23 | 24 | string.punctuation: 25 | '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~' 26 | 27 | string.printable: 28 | '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c' 29 | -------------------------------------------------------------------------------- /bin/install-python.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This downloads and install python3.4 to the directory passed in 3 | 4 | VERSION=3.4.9 5 | DEST=$1 6 | 7 | if [ "$DEST" = "" ]; then 8 | echo "Syntax: $0 " 9 | exit 1 10 | fi 11 | 12 | if [ -e "$DEST/bin/python3.4" ]; then 13 | echo "Python already installed in $DEST - skipping install" 14 | exit 0 15 | fi 16 | 17 | mkdir -p $DEST 18 | 19 | cd /tmp 20 | curl https://www.python.org/ftp/python/${VERSION}/Python-${VERSION}.tar.xz --output Python-${VERSION}.tar.xz --silent 21 | tar Jxf Python-${VERSION}.tar.xz 22 | cd Python-${VERSION} 23 | ./configure --prefix=$DEST 24 | make 25 | make install 26 | echo "Python $VERSION installed in $DEST" 27 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | # .goreleaser.yml 2 | # Build customization 3 | builds: 4 | - binary: gpython 5 | goos: 6 | - windows 7 | - darwin 8 | - linux 9 | - freebsd 10 | - netbsd 11 | goarch: 12 | - amd64 13 | - 386 14 | - arm 15 | - arm64 16 | 17 | snapshot: 18 | # Allows you to change the name of the generated snapshot 19 | # releases. The following variables are available: 20 | # - Commit 21 | # - Tag 22 | # - Timestamp 23 | # Default is `SNAPSHOT-{{.Commit}}`. 24 | name_template: "{{.Tag}}-DEV" 25 | 26 | # Archive customization 27 | archive: 28 | format: zip 29 | replacements: 30 | darwin: macOS 31 | files: 32 | - README.md 33 | - LICENSE 34 | -------------------------------------------------------------------------------- /examples/embedding/testdata/mylib-demo_golden.txt: -------------------------------------------------------------------------------- 1 | 2 | Welcome to a gpython embedded example, 3 | where your wildest Go-based python dreams come true! 4 | 5 | ========================================================== 6 | Python 3.4 (github.com/go-python/gpython) 7 | ========================================================== 8 | 9 | Spring Break itinerary: 10 | Stop 1: Miami, Florida | 7 nights 11 | Stop 2: Mallorca, Spain | 3 nights 12 | Stop 3: Ibiza, Spain | 14 nights 13 | Stop 4: Monaco | 12 nights 14 | ### Made with Vacation 1.0 by Fletch F. Fletcher 15 | 16 | I bet Monaco will be the best! 17 | 18 | <<< host py.Context of py.Module instance closing >>> 19 | +++ 20 | -------------------------------------------------------------------------------- /repl/web/wasm_exec.js.patch: -------------------------------------------------------------------------------- 1 | --- /opt/go/go-tip/misc/wasm/wasm_exec.js 2018-10-09 16:22:04.204754982 +0100 2 | +++ wasm_exec.js 2018-10-09 16:19:31.739950505 +0100 3 | @@ -2,6 +2,10 @@ 4 | // Use of this source code is governed by a BSD-style 5 | // license that can be found in the LICENSE file. 6 | 7 | +// Slightly modified by ncw to: 8 | +// * add empty implementation of fsyncSync 9 | +// See wasm_exec.js.patch for details 10 | + 11 | (() => { 12 | // Map web browser API and Node.js API to a single common API (preferring web standards over Node.js API). 13 | const isNodeJS = typeof process !== "undefined"; 14 | @@ -52,6 +56,8 @@ 15 | err.code = "ENOSYS"; 16 | throw err; 17 | }, 18 | + fsyncSync(fd) { 19 | + }, 20 | }; 21 | } 22 | 23 | -------------------------------------------------------------------------------- /pytest/pytest_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package pytest 6 | 7 | import ( 8 | "testing" 9 | ) 10 | 11 | func TestCompileSrc(t *testing.T) { 12 | for _, tc := range []struct { 13 | name string 14 | code string 15 | }{ 16 | { 17 | name: "hello", 18 | code: `print("hello")`, 19 | }, 20 | } { 21 | t.Run(tc.name, func(t *testing.T) { 22 | _, _ = CompileSrc(t, gContext, tc.code, tc.name) 23 | }) 24 | } 25 | } 26 | 27 | func TestRunTests(t *testing.T) { 28 | RunTests(t, "./testdata/tests") 29 | } 30 | 31 | func TestRunScript(t *testing.T) { 32 | RunScript(t, "./testdata/hello.py") 33 | } 34 | -------------------------------------------------------------------------------- /py/tests/complex.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | from libtest import assertRaises 6 | 7 | doc="str" 8 | assert str(3+4j) == "(3+4j)" 9 | 10 | doc="repr" 11 | assert repr(3+4j) == "(3+4j)" 12 | 13 | doc="real" 14 | assert (3+4j).real == 3.0 15 | 16 | doc="imag" 17 | assert (3+4j).imag == 4.0 18 | 19 | doc="conjugate" 20 | assert (3+4j).conjugate() == 3-4j 21 | 22 | doc="add" 23 | assert (3+4j) + 2 == 5+4j 24 | assert (3+4j) + 2j == 3+6j 25 | 26 | doc="sub" 27 | assert (3+4j) - 1 == 2+4j 28 | assert (3+4j) - 1j == 3+3j 29 | 30 | doc="mul" 31 | assert (3+4j) * 2 == 6+8j 32 | assert (3+4j) * 2j == -8+6j 33 | 34 | doc="finished" 35 | -------------------------------------------------------------------------------- /py/tests/internal.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | from libtest import assertRaises 6 | 7 | def fn(x): 8 | return x 9 | 10 | doc="check internal bound methods" 11 | assert (1).__str__() == "1" 12 | assert (1).__add__(2) == 3 13 | assert fn.__call__(4) == 4 14 | assert fn.__get__(fn, None)()(1) == 1 15 | assertRaises(TypeError, fn.__get__, fn, None, None) 16 | # These tests don't work on python3.4 17 | # assert Exception().__getattr__("a") is not None # check doesn't explode only 18 | # assertRaises(TypeError, Exception().__getattr__, "a", "b") 19 | # assertRaises(ValueError, Exception().__getattr__, 42) 20 | 21 | doc="finished" 22 | 23 | -------------------------------------------------------------------------------- /py/tests/slice.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | doc="slice" 6 | a = slice(10) 7 | assert a.start == None 8 | assert a.stop == 10 9 | assert a.step == None 10 | 11 | a = slice(0, 10, 1) 12 | assert a.start == 0 13 | assert a.stop == 10 14 | assert a.step == 1 15 | 16 | assert slice(1).__eq__(slice(1)) 17 | assert slice(1) != slice(2) 18 | assert slice(1) == slice(None, 1, None) 19 | assert slice(0, 0, 0) == slice(0, 0, 0) 20 | 21 | assert slice(0, 0, 1) != slice(0, 0, 0) 22 | assert slice(0, 1, 0) != slice(0, 0, 0) 23 | assert slice(1, 0, 0) != slice(0, 0, 0) 24 | assert slice(0).__ne__(slice(1)) 25 | assert slice(0, None, 3).__ne__(slice(0, 0, 3)) 26 | 27 | doc="finished" -------------------------------------------------------------------------------- /vm/tests/import_star.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | # test IMPORT_STAR 6 | 7 | from lib import * 8 | 9 | assert libfn() == 42 10 | assert libvar == 43 11 | assert libclass().method() == 44 12 | 13 | ok = False 14 | try: 15 | _libprivate 16 | except NameError: 17 | ok = True 18 | assert ok 19 | 20 | from lib1 import * 21 | 22 | assert lib1fn() == 42 23 | assert lib1var == 43 24 | 25 | doc="IMPORT_START 1" 26 | ok = False 27 | try: 28 | lib1class 29 | except NameError: 30 | ok = True 31 | assert ok 32 | 33 | doc="IMPORT_START 2" 34 | ok = False 35 | try: 36 | _libprivate 37 | except NameError: 38 | ok = True 39 | assert ok 40 | 41 | doc="finished" 42 | -------------------------------------------------------------------------------- /vm/tests/attr.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | class C: 6 | attr1 = 42 7 | def __init__(self): 8 | self.attr2 = 43 9 | self.attr3 = 44 10 | c = C() 11 | 12 | doc="Test LOAD_ATTR" 13 | assert c.attr1 == 42 14 | assert C.attr1 == 42 15 | assert c.attr2 == 43 16 | assert c.attr3 == 44 17 | 18 | doc="Test DELETE_ATTR" 19 | del c.attr3 20 | 21 | ok = False 22 | try: 23 | c.attr3 24 | except AttributeError: 25 | ok = True 26 | assert ok 27 | 28 | doc="Test STORE_ATTR" 29 | c.attr1 = 100 30 | c.attr2 = 101 31 | c.attr3 = 102 32 | assert c.attr1 == 100 33 | assert C.attr1 == 42 34 | assert c.attr2 == 101 35 | assert c.attr3 == 102 36 | 37 | doc="finished" 38 | -------------------------------------------------------------------------------- /py/tests/iter.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | doc="iter" 6 | cnt = 0 7 | def f(): 8 | global cnt 9 | cnt += 1 10 | return cnt 11 | 12 | l = list(iter(f,20)) 13 | assert len(l) == 19 14 | for idx, v in enumerate(l): 15 | assert idx + 1 == v 16 | 17 | words1 = ['g', 'p', 'y', 't', 'h', 'o', 'n'] 18 | words2 = list(iter(words1)) 19 | for w1, w2 in zip(words1, words2): 20 | assert w1 == w2 21 | 22 | class SequenceClass: 23 | def __init__(self, n): 24 | self.n = n 25 | def __getitem__(self, i): 26 | if 0 <= i < self.n: 27 | return i 28 | else: 29 | raise IndexError 30 | 31 | assert list(iter(SequenceClass(5))) == [0, 1, 2, 3, 4] 32 | 33 | doc="finished" -------------------------------------------------------------------------------- /py/tests/libtest.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | """ 6 | Simple test harness 7 | """ 8 | 9 | def assertRaises(expecting, fn, *args, **kwargs): 10 | """Check the exception was raised - don't check the text""" 11 | try: 12 | fn(*args, **kwargs) 13 | except expecting as e: 14 | pass 15 | else: 16 | assert False, "%s not raised" % (expecting,) 17 | 18 | def assertRaisesText(expecting, text, fn, *args, **kwargs): 19 | """Check the exception with text in is raised""" 20 | try: 21 | fn(*args, **kwargs) 22 | except expecting as e: 23 | assert text in e.args[0], "'%s' not found in '%s'" % (text, e.args[0]) 24 | else: 25 | assert False, "%s not raised" % (expecting,) 26 | -------------------------------------------------------------------------------- /examples/embedding/testdata/mylib-demo.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | print(''' 6 | Welcome to a gpython embedded example, 7 | where your wildest Go-based python dreams come true!''') 8 | 9 | # This is a model for a public/user-side script that you or users would maintain, 10 | # offering an open canvas to drive app behavior, customization, or anything you can dream up. 11 | # 12 | # Modules you offer for consumption can also serve to document such things. 13 | from mylib import * 14 | 15 | springBreak = Vacation("Spring Break", Stop("Miami, Florida", 7), Stop("Mallorca, Spain", 3)) 16 | springBreak.AddStops(Stop("Ibiza, Spain", 14), Stop("Monaco", 12)) 17 | springBreak.PrintItinerary() 18 | 19 | print("\nI bet %s will be the best!\n" % springBreak.GetStop(4).Get()[0]) 20 | -------------------------------------------------------------------------------- /py/range_repr19.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build !go1.10 6 | // +build !go1.10 7 | 8 | // Range object 9 | 10 | package py 11 | 12 | import "bytes" 13 | 14 | func (r *Range) repr() (Object, error) { 15 | var b bytes.Buffer 16 | b.WriteString("range(") 17 | start, err := ReprAsString(r.Start) 18 | if err != nil { 19 | return nil, err 20 | } 21 | stop, err := ReprAsString(r.Stop) 22 | if err != nil { 23 | return nil, err 24 | } 25 | b.WriteString(start) 26 | b.WriteString(", ") 27 | b.WriteString(stop) 28 | 29 | if r.Step != 1 { 30 | step, err := ReprAsString(r.Step) 31 | if err != nil { 32 | return nil, err 33 | } 34 | b.WriteString(", ") 35 | b.WriteString(step) 36 | } 37 | b.WriteString(")") 38 | 39 | return String(b.String()), nil 40 | } 41 | -------------------------------------------------------------------------------- /vm/tests/comprehensions.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | doc="List comprehensions" 6 | A = [1,2,3,4] 7 | B = [ 2*a for a in A ] 8 | assert tuple(B) == tuple([2,4,6,8]) 9 | B = [ 2*a for a in A if a != 2] 10 | assert tuple(B) == tuple([2,6,8]) 11 | 12 | # FIXME - exitYield not working? 13 | doc="Generator expressions" 14 | A = (1,2,3,4) 15 | B = ( 2*a for a in A ) 16 | assert tuple(B) == (2,4,6,8) 17 | B = [ 2*a for a in A if a != 2] 18 | assert tuple(B) == (2,6,8) 19 | 20 | doc="Set comprehensions" 21 | A = {1,2,3,4} 22 | B = { 2*a for a in A } 23 | assert B == {2,4,6,8} 24 | B = { 2*a for a in A if a != 2} 25 | assert B == {2,6,8} 26 | 27 | doc="Dict comprehensions" 28 | A = {"a":1, "b":2, "c":3} 29 | B = { k:k for k in ("a","b","c") } 30 | assert B["b"] == "b" 31 | 32 | doc="finished" 33 | -------------------------------------------------------------------------------- /py/range_repr110.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build go1.10 6 | // +build go1.10 7 | 8 | // Range object 9 | 10 | package py 11 | 12 | import "strings" 13 | 14 | func (r *Range) repr() (Object, error) { 15 | var b strings.Builder 16 | b.WriteString("range(") 17 | start, err := ReprAsString(r.Start) 18 | if err != nil { 19 | return nil, err 20 | } 21 | stop, err := ReprAsString(r.Stop) 22 | if err != nil { 23 | return nil, err 24 | } 25 | b.WriteString(start) 26 | b.WriteString(", ") 27 | b.WriteString(stop) 28 | 29 | if r.Step != 1 { 30 | step, err := ReprAsString(r.Step) 31 | if err != nil { 32 | return nil, err 33 | } 34 | b.WriteString(", ") 35 | b.WriteString(step) 36 | } 37 | b.WriteString(")") 38 | 39 | return String(b.String()), nil 40 | } 41 | -------------------------------------------------------------------------------- /compile/diffdis.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3.4 2 | 3 | # Copyright 2018 The go-python Authors. All rights reserved. 4 | # Use of this source code is governed by a BSD-style 5 | # license that can be found in the LICENSE file. 6 | 7 | # Diff two bytecode strings 8 | 9 | import sys 10 | import io 11 | import os 12 | from dis import dis 13 | from difflib import unified_diff 14 | from tempfile import NamedTemporaryFile 15 | 16 | def disassemble(code): 17 | """Disassemble code into string""" 18 | out = io.StringIO() 19 | dis(code, file=out) 20 | return out.getvalue() 21 | 22 | def main(): 23 | assert len(sys.argv) == 3, "Need two arguments" 24 | a, b = (bytes(x, "latin1").decode("unicode_escape").encode("latin1") for x in sys.argv[1:]) 25 | a_dissasembly = disassemble(a) 26 | b_dissasembly = disassemble(b) 27 | for line in unified_diff(a_dissasembly.split("\n"), b_dissasembly.split("\n"), fromfile="want", tofile="got"): 28 | print(line) 29 | 30 | if __name__ == "__main__": 31 | main() 32 | -------------------------------------------------------------------------------- /py/cell.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Cell object 6 | // 7 | // In the Go implementation this is just a pointer to an Object which 8 | // can be nil 9 | 10 | package py 11 | 12 | // A python Cell object 13 | type Cell struct { 14 | obj *Object 15 | } 16 | 17 | var CellType = NewType("cell", "cell object") 18 | 19 | // Type of this object 20 | func (o *Cell) Type() *Type { 21 | return CellType 22 | } 23 | 24 | // Define a new cell 25 | func NewCell(obj Object) *Cell { 26 | return &Cell{&obj} 27 | } 28 | 29 | // Fetch the contents of the Cell or nil if not set 30 | func (c *Cell) Get() Object { 31 | if c.obj == nil { 32 | return nil 33 | } 34 | return *c.obj 35 | } 36 | 37 | // Set the contents of the Cell 38 | func (c *Cell) Set(obj Object) { 39 | c.obj = &obj 40 | } 41 | 42 | // Delete the contents of the Cell 43 | func (c *Cell) Delete() { 44 | c.obj = nil 45 | } 46 | -------------------------------------------------------------------------------- /symtable/stringer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // generated by stringer -type=Scope,BlockType -output stringer.go; DO NOT EDIT 6 | 7 | package symtable 8 | 9 | import "fmt" 10 | 11 | const _Scope_name = "ScopeInvalidScopeLocalScopeGlobalExplicitScopeGlobalImplicitScopeFreeScopeCell" 12 | 13 | var _Scope_index = [...]uint8{0, 12, 22, 41, 60, 69, 78} 14 | 15 | func (i Scope) String() string { 16 | if i+1 >= Scope(len(_Scope_index)) { 17 | return fmt.Sprintf("Scope(%d)", i) 18 | } 19 | return _Scope_name[_Scope_index[i]:_Scope_index[i+1]] 20 | } 21 | 22 | const _BlockType_name = "FunctionBlockClassBlockModuleBlock" 23 | 24 | var _BlockType_index = [...]uint8{0, 13, 23, 34} 25 | 26 | func (i BlockType) String() string { 27 | if i+1 >= BlockType(len(_BlockType_index)) { 28 | return fmt.Sprintf("BlockType(%d)", i) 29 | } 30 | return _BlockType_name[_BlockType_index[i]:_BlockType_index[i+1]] 31 | } 32 | -------------------------------------------------------------------------------- /stdlib/os/testdata/test_golden.txt: -------------------------------------------------------------------------------- 1 | test os 2 | os.error: 3 | os.getenv($GPYTHON_TEST_HOME)= None 4 | os.environ($GPYTHON_TEST_HOME)= None 5 | os.getenv($GPYTHON_TEST_HOME)= /home/go 6 | os.unsetenv($GPYTHON_TEST_HOME)= None 7 | os.error is OSError [OK] 8 | os.getcwd() != None [OK] 9 | os.getcwdb() != None [OK] 10 | os.system('echo hello')... 11 | hello 12 | 13 | os.getpid is greater than 1 [OK] 14 | os.chdir(testdir) [OK] 15 | os.chdir(1) failed [OK] 16 | os.environ.get(15) failed [OK] 17 | os.putenv() failed [OK] 18 | os.unsetenv() failed [OK] 19 | os.getenv() failed [OK] 20 | os.unsetenv("FOO", "BAR") failed [OK] 21 | bytes(os.getcwd(), "utf-8") == os.getcwdb() [OK] 22 | os.sep: [OK] 23 | os.pathsep: [OK] 24 | os.linesep: [OK] 25 | os.devnull: [OK] 26 | os.altsep: [OK] 27 | caught: OSError: 'Bad file descriptor' [OK] 28 | [b'dir1', b'dir2'] 29 | ['dir1', 'dir2'] 30 | caught: SystemError - no such file or directory [OK] 31 | caught: FileExistsError [OK] 32 | caught: SystemError - directory not empty [OK] 33 | ['dir1'] 34 | os.{mkdir,rmdir,remove,removedirs} worked as expected 35 | OK 36 | -------------------------------------------------------------------------------- /py/tests/file.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | from libtest import assertRaises 6 | 7 | doc = "open" 8 | assertRaises(FileNotFoundError, open, "not-existent.file") 9 | 10 | assertRaises(IsADirectoryError, open, ".") 11 | 12 | f = open(__file__) 13 | assert f is not None 14 | 15 | doc = "read" 16 | b = f.read(12) 17 | assert b == '# Copyright ' 18 | 19 | b = f.read(4) 20 | assert b == '2018' 21 | 22 | b = f.read() 23 | assert b != '' 24 | 25 | b = f.read() 26 | assert b == '' 27 | 28 | doc = "write" 29 | assertRaises(TypeError, f.write, 42) 30 | 31 | # assertRaises(io.UnsupportedOperation, f.write, 'hello') 32 | 33 | import sys 34 | n = sys.stdout.write('hello') 35 | assert n == 5 36 | 37 | doc = "close" 38 | assert f.close() == None 39 | 40 | assertRaises(ValueError, f.read, 1) 41 | assertRaises(ValueError, f.write, "") 42 | assertRaises(ValueError, f.flush) 43 | 44 | # closing a closed file should not throw an error 45 | assert f.close() == None 46 | 47 | doc = "finished" 48 | -------------------------------------------------------------------------------- /py/ellipsis.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Ellipsis objects 6 | 7 | package py 8 | 9 | type EllipsisType struct{} 10 | 11 | var ( 12 | EllipsisTypeType = NewType("EllipsisType", "") 13 | Ellipsis = EllipsisType(struct{}{}) 14 | ) 15 | 16 | // Type of this object 17 | func (s EllipsisType) Type() *Type { 18 | return EllipsisTypeType 19 | } 20 | 21 | func (a EllipsisType) M__bool__() (Object, error) { 22 | return False, nil 23 | } 24 | 25 | func (a EllipsisType) M__repr__() (Object, error) { 26 | return String("Ellipsis"), nil 27 | } 28 | 29 | func (a EllipsisType) M__eq__(other Object) (Object, error) { 30 | if _, ok := other.(EllipsisType); ok { 31 | return True, nil 32 | } 33 | return False, nil 34 | } 35 | 36 | func (a EllipsisType) M__ne__(other Object) (Object, error) { 37 | if _, ok := other.(EllipsisType); ok { 38 | return False, nil 39 | } 40 | return True, nil 41 | } 42 | 43 | // Check interface is satisfied 44 | var _ I__bool__ = Ellipsis 45 | var _ I__repr__ = Ellipsis 46 | var _ I__eq__ = Ellipsis 47 | var _ I__eq__ = Ellipsis 48 | -------------------------------------------------------------------------------- /stdlib/string/testdata/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | import string 6 | 7 | print("globals:") 8 | for name in ("whitespace", 9 | "ascii_lowercase", 10 | "ascii_uppercase", 11 | "ascii_letters", 12 | "digits", 13 | "hexdigits", 14 | "octdigits", 15 | "punctuation", 16 | "printable"): 17 | v = getattr(string, name) 18 | print("\nstring.%s:\n%s" % (name,repr(v))) 19 | 20 | def assertEqual(x, y): 21 | assert x == y, "got: %s, want: %s" % (repr(x), repr(y)) 22 | 23 | assertEqual(string.capwords('abc def ghi'), 'Abc Def Ghi') 24 | assertEqual(string.capwords('abc\tdef\nghi'), 'Abc Def Ghi') 25 | assertEqual(string.capwords('abc\t def \nghi'), 'Abc Def Ghi') 26 | assertEqual(string.capwords('ABC DEF GHI'), 'Abc Def Ghi') 27 | assertEqual(string.capwords('ABC-DEF-GHI', '-'), 'Abc-Def-Ghi') 28 | assertEqual(string.capwords('ABC-def DEF-ghi GHI'), 'Abc-def Def-ghi Ghi') 29 | assertEqual(string.capwords(' aBc DeF '), 'Abc Def') 30 | assertEqual(string.capwords('\taBc\tDeF\t'), 'Abc Def') 31 | assertEqual(string.capwords('\taBc\tDeF\t', '\t'), '\tAbc\tDef\t') 32 | 33 | -------------------------------------------------------------------------------- /py/call_iterator.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // CallIterator objects 6 | 7 | package py 8 | 9 | // A python CallIterator object 10 | type CallIterator struct { 11 | callable Object 12 | sentinel Object 13 | } 14 | 15 | var CallIteratorType = NewType("callable_iterator", "callable_iterator type") 16 | 17 | // Type of this object 18 | func (o *CallIterator) Type() *Type { 19 | return CallIteratorType 20 | } 21 | 22 | func (cit *CallIterator) M__iter__() (Object, error) { 23 | return cit, nil 24 | } 25 | 26 | // Get next one from the iteration 27 | func (cit *CallIterator) M__next__() (Object, error) { 28 | value, err := Call(cit.callable, nil, nil) 29 | 30 | if err != nil { 31 | return nil, err 32 | } 33 | 34 | if value == cit.sentinel { 35 | return nil, StopIteration 36 | } 37 | 38 | return value, nil 39 | } 40 | 41 | // Define a new CallIterator 42 | func NewCallIterator(callable Object, sentinel Object) *CallIterator { 43 | c := &CallIterator{ 44 | callable: callable, 45 | sentinel: sentinel, 46 | } 47 | return c 48 | } 49 | 50 | // Check interface is satisfied 51 | var _ I_iterator = (*CallIterator)(nil) 52 | -------------------------------------------------------------------------------- /repl/web/loader.js: -------------------------------------------------------------------------------- 1 | // Set the default global log for use by wasm_exec.js 2 | go_log = console.log; 3 | 4 | var useWasm = location.href.includes("?wasm"); 5 | 6 | console.log("useWasm =", useWasm); 7 | 8 | var script = document.createElement('script'); 9 | if (useWasm) { 10 | script.src = "wasm_exec.js"; 11 | script.onload = function () { 12 | // polyfill 13 | if (!WebAssembly.instantiateStreaming) { 14 | WebAssembly.instantiateStreaming = async (resp, importObject) => { 15 | const source = await (await resp).arrayBuffer(); 16 | return await WebAssembly.instantiate(source, importObject); 17 | }; 18 | } 19 | 20 | const go = new Go(); 21 | let mod, inst; 22 | WebAssembly.instantiateStreaming(fetch("gpython.wasm"), go.importObject).then((result) => { 23 | mod = result.module; 24 | inst = result.instance; 25 | run(); 26 | }); 27 | 28 | async function run() { 29 | console.clear(); 30 | await go.run(inst); 31 | inst = await WebAssembly.instantiate(mod, go.importObject); // reset instance 32 | } 33 | }; 34 | } else { 35 | script.src = "gpython.js"; 36 | } 37 | document.head.appendChild(script); 38 | -------------------------------------------------------------------------------- /examples/chudtest.txt: -------------------------------------------------------------------------------- 1 | gpython 2 | ------- 3 | 4 | 31415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679 5 | chudnovsky_gmpy_bs: digits 10 time 0.00010633468627929688 6 | chudnovsky_gmpy_bs: digits 100 time 0.0003457069396972656 7 | Last 5 digits 70679 OK 8 | chudnovsky_gmpy_bs: digits 1000 time 0.005132913589477539 9 | Last 5 digits 01989 OK 10 | chudnovsky_gmpy_bs: digits 10000 time 0.05408167839050293 11 | Last 5 digits 75678 OK 12 | chudnovsky_gmpy_bs: digits 100000 time 2.7544572353363037 13 | Last 5 digits 24646 OK 14 | chudnovsky_gmpy_bs: digits 1000000 time 298.565589427948 15 | Last 5 digits 58151 OK 16 | 17 | python3 18 | ------- 19 | 31415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679 20 | chudnovsky_gmpy_bs: digits 10 time 2.0265579223632812e-05 21 | chudnovsky_gmpy_bs: digits 100 time 5.8650970458984375e-05 22 | Last 5 digits 70679 OK 23 | chudnovsky_gmpy_bs: digits 1000 time 0.0009014606475830078 24 | Last 5 digits 01989 OK 25 | chudnovsky_gmpy_bs: digits 10000 time 0.06584382057189941 26 | Last 5 digits 75678 OK 27 | chudnovsky_gmpy_bs: digits 100000 time 6.900329828262329 28 | Last 5 digits 24646 OK 29 | chudnovsky_gmpy_bs: digits 1000000 time 791.7894506454468 30 | Last 5 digits 58151 OK 31 | -------------------------------------------------------------------------------- /stdlib/math/tests/libulp.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | try: 6 | from math import to_ulps 7 | except ImportError: 8 | import struct 9 | def to_ulps(x): 10 | """Convert a non-NaN float x to an integer, in such a way that 11 | adjacent floats are converted to adjacent integers. Then 12 | abs(ulps(x) - ulps(y)) gives the difference in ulps between two 13 | floats. 14 | 15 | The results from this function will only make sense on platforms 16 | where C doubles are represented in IEEE 754 binary64 format. 17 | 18 | """ 19 | n = struct.unpack(' 2 | #include "Python-ast.h" 3 | #include "code.h" 4 | #include "symtable.h" 5 | #include "structmember.h" 6 | 7 | static PyObject* 8 | readsymtab(PyObject* self, PyObject* args) 9 | { 10 | PyObject* obj; 11 | 12 | if (!PyArg_ParseTuple(args, "O", &obj)) 13 | return NULL; 14 | 15 | if (Py_TYPE(obj) != &PySTEntry_Type) { 16 | fprintf(stderr, "Ooops wrong type\n"); 17 | } 18 | 19 | PySTEntryObject *st = (PySTEntryObject *)obj; 20 | 21 | return Py_BuildValue("(iiiiiii)", 22 | st->ste_free, 23 | st->ste_child_free, 24 | st->ste_generator, 25 | st->ste_varargs, 26 | st->ste_varkeywords, 27 | st->ste_returns_value, 28 | st->ste_needs_class_closure); 29 | } 30 | 31 | static PyMethodDef readsymtabmethods[] = 32 | { 33 | {"readsymtab", readsymtab, METH_VARARGS, "Read the symbol table."}, 34 | {NULL, NULL, 0, NULL} 35 | }; 36 | 37 | static struct PyModuleDef readsymtabmodule = { 38 | PyModuleDef_HEAD_INIT, 39 | "readsymtab", 40 | "Read the symbol table.", 41 | -1, 42 | readsymtabmethods, 43 | NULL, 44 | NULL, 45 | NULL, 46 | NULL 47 | }; 48 | 49 | 50 | PyMODINIT_FUNC 51 | PyInit_readsymtab(void) 52 | { 53 | return PyModule_Create(&readsymtabmodule); 54 | } 55 | -------------------------------------------------------------------------------- /vm/vm.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Python virtual machine 6 | package vm 7 | 8 | import ( 9 | "github.com/go-python/gpython/py" 10 | ) 11 | 12 | //go:generate stringer -type=vmStatus,OpCode -output stringer.go 13 | 14 | // VM status code 15 | type vmStatus byte 16 | 17 | // VM Status code for main loop (reason for stack unwind) 18 | const ( 19 | whyNot vmStatus = iota // No error 20 | whyException // Exception occurred 21 | whyReturn // 'return' statement 22 | whyBreak // 'break' statement 23 | whyContinue // 'continue' statement 24 | whyYield // 'yield' operator 25 | whySilenced // Exception silenced by 'with' 26 | ) 27 | 28 | // Virtual machine state 29 | type Vm struct { 30 | // Current frame 31 | frame *py.Frame 32 | // Whether ext should be added to the next arg 33 | extended bool 34 | // 16 bit extension for argument for next opcode 35 | ext int32 36 | // Return value 37 | retval py.Object 38 | // VM Status code for main loop 39 | why vmStatus 40 | // Current Pending exception type, value and traceback 41 | curexc py.ExceptionInfo 42 | // Previous exception type, value and traceback 43 | exc py.ExceptionInfo 44 | // VM access to state / modules 45 | context py.Context 46 | } 47 | -------------------------------------------------------------------------------- /py/type_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package py 6 | 7 | import "testing" 8 | 9 | func TestIsSubType(t *testing.T) { 10 | for _, test := range []struct { 11 | a *Type 12 | b *Type 13 | want bool 14 | }{ 15 | {ValueError, ValueError, true}, 16 | {ValueError, ExceptionType, true}, 17 | {ExceptionType, ValueError, false}, 18 | } { 19 | got := test.a.IsSubtype(test.b) 20 | if test.want != got { 21 | t.Errorf("%v.IsSubtype(%v) want %v got %v", test.a.Name, test.b.Name, test.want, got) 22 | } 23 | } 24 | } 25 | 26 | func TestMro(t *testing.T) { 27 | for _, test := range []struct { 28 | t *Type 29 | want []*Type 30 | }{ 31 | {ObjectType, []*Type{ObjectType}}, 32 | {BaseException, []*Type{BaseException, ObjectType}}, 33 | {ExceptionType, []*Type{ExceptionType, BaseException, ObjectType}}, 34 | {ValueError, []*Type{ValueError, ExceptionType, BaseException, ObjectType}}, 35 | } { 36 | got := test.t.Mro 37 | if len(test.want) != len(got) { 38 | t.Errorf("differing lengths: want %v, got %v", test.want, got) 39 | } else { 40 | for i := range got { 41 | baseGot := got[i].(*Type) 42 | baseWant := test.want[i] 43 | if baseGot != baseWant { 44 | t.Errorf("mro[%d] want %s got %s", i, baseWant.Name, baseGot.Name) 45 | } 46 | 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /examples/embedding/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "flag" 9 | "fmt" 10 | 11 | // This initializes gpython for runtime execution and is essential. 12 | // It defines forward-declared symbols and registers native built-in modules, such as sys and time. 13 | _ "github.com/go-python/gpython/stdlib" 14 | 15 | // Commonly consumed gpython 16 | "github.com/go-python/gpython/py" 17 | "github.com/go-python/gpython/repl" 18 | "github.com/go-python/gpython/repl/cli" 19 | ) 20 | 21 | func main() { 22 | flag.Parse() 23 | runWithFile(flag.Arg(0)) 24 | } 25 | 26 | func runWithFile(pyFile string) error { 27 | 28 | // See type Context interface and related docs 29 | ctx := py.NewContext(py.DefaultContextOpts()) 30 | 31 | // This drives modules being able to perform cleanup and release resources 32 | defer ctx.Close() 33 | 34 | var err error 35 | if len(pyFile) == 0 { 36 | replCtx := repl.New(ctx) 37 | 38 | fmt.Print("\n======= Entering REPL mode, press Ctrl+D to exit =======\n") 39 | 40 | _, err = py.RunFile(ctx, "lib/REPL-startup.py", py.CompileOpts{}, replCtx.Module) 41 | if err == nil { 42 | cli.RunREPL(replCtx) 43 | } 44 | 45 | } else { 46 | _, err = py.RunFile(ctx, pyFile, py.CompileOpts{}, nil) 47 | } 48 | 49 | if err != nil { 50 | py.TracebackDump(err) 51 | } 52 | 53 | return err 54 | } 55 | -------------------------------------------------------------------------------- /vm/tests/class.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | doc="Test class definitions" 6 | class C1: 7 | "Test 1" 8 | def method1(self, x): 9 | "method1" 10 | return x+1 11 | def method2(self, m2): 12 | "method2" 13 | return self.method1(m2)+m2 14 | 15 | c = C1() 16 | assert c.method1(1) == 2 17 | assert c.method2(1) == 3 18 | 19 | doc="Test class definitions 2" 20 | class C2: 21 | "Test 2" 22 | _VAR = 1 23 | VAR = _VAR + 1 24 | def method1(self, x): 25 | "method1" 26 | return self.VAR + x 27 | def method2(self, m2): 28 | "method2" 29 | return self.method1(m2)+m2 30 | 31 | c = C2() 32 | assert c.method1(1) == 3 33 | assert c.method2(1) == 4 34 | 35 | # FIXME more corner cases in CLASS_DEREF 36 | 37 | doc="CLASS_DEREF" 38 | def classderef(y): 39 | x = y 40 | class DeRefTest: 41 | VAR = x 42 | def method1(self, x): 43 | "method1" 44 | return self.VAR+x 45 | return DeRefTest 46 | x = classderef(1) 47 | c = x() 48 | assert c.method1(1) == 2 49 | 50 | doc="CLASS_DEREF2" 51 | def classderef2(x): 52 | class DeRefTest: 53 | VAR = x 54 | def method1(self, x): 55 | "method1" 56 | return self.VAR+x 57 | return DeRefTest 58 | x = classderef2(1) 59 | c = x() 60 | assert c.method1(1) == 2 61 | 62 | doc="finished" 63 | -------------------------------------------------------------------------------- /vm/tests/call.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | doc="CALL_FUNCTION" 6 | def fn1(a, b=None): 7 | assert a == 1 8 | assert b == 2 9 | fn1(1, 2) 10 | fn1(1, b=2) 11 | fn1(a=1, b=2) 12 | 13 | ok = False 14 | try: 15 | fn1(1, 2, c=4) 16 | except TypeError as e: 17 | assert e.args[0] == "fn1() got an unexpected keyword argument 'c'" 18 | ok = True 19 | assert ok, "TypeError not raised" 20 | 21 | ok = False 22 | try: 23 | fn1(1,*(2,3,4),b=42) 24 | except TypeError as e: 25 | assert e.args[0] == "fn1() got multiple values for argument 'b'" 26 | ok = True 27 | assert ok, "TypeError not raised" 28 | 29 | doc="CALL_FUNCTION_VAR" 30 | def fn2(a, b=None, *args): 31 | assert a == 1 32 | assert b == 2 33 | assert args == (3, 4) 34 | fn2(1,2,*(3,4)) 35 | fn2(1,*(2,3,4)) 36 | fn2(*(1,2,3,4)) 37 | 38 | doc="CALL_FUNCTION_KW" 39 | def fn3(a, b=None, **kwargs): 40 | assert a == 1 41 | assert b == 2 42 | assert kwargs['c'] == 3 43 | assert kwargs['d'] == 4 44 | fn3(1, 2, **{'c':3, 'd':4}) 45 | fn3(1, **{'b':2, 'c':3, 'd':4}) 46 | fn3(**{'a':1, 'b':2, 'c':3, 'd':4}) 47 | 48 | doc="CALL_FUNCTION_VAR_KW" 49 | def fn4(a, b=None, *args, **kwargs): 50 | assert a == 1 51 | assert b == 2 52 | assert args == (3, 4) 53 | assert kwargs['c'] == 5 54 | assert kwargs['d'] == 6 55 | fn4(1, 2, *(3, 4), **{'c':5, 'd':6}) 56 | 57 | doc="finished" 58 | -------------------------------------------------------------------------------- /compile/instructions_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package compile 6 | 7 | import ( 8 | "bytes" 9 | "testing" 10 | ) 11 | 12 | func TestLnotab(t *testing.T) { 13 | for i, test := range []struct { 14 | instrs Instructions 15 | want []byte 16 | }{ 17 | { 18 | instrs: Instructions{}, 19 | want: []byte{}, 20 | }, 21 | { 22 | instrs: Instructions{ 23 | &Op{pos: pos{n: 1, p: 10, lineno: 1}}, 24 | &Op{pos: pos{n: 0, p: 10, lineno: 0}}, 25 | &Op{pos: pos{n: 1, p: 102, lineno: 1}}, 26 | }, 27 | want: []byte{}, 28 | }, 29 | { 30 | instrs: Instructions{ 31 | &Op{pos: pos{n: 1, p: 0, lineno: 1}}, 32 | &Op{pos: pos{n: 1, p: 1, lineno: 2}}, 33 | &Op{pos: pos{n: 1, p: 2, lineno: 3}}, 34 | }, 35 | want: []byte{1, 1, 1, 1}, 36 | }, 37 | { 38 | // Example from lnotab.txt 39 | instrs: Instructions{ 40 | &Op{pos: pos{n: 1, p: 0, lineno: 1}}, 41 | &Op{pos: pos{n: 1, p: 6, lineno: 2}}, 42 | &Op{pos: pos{n: 1, p: 50, lineno: 7}}, 43 | &Op{pos: pos{n: 1, p: 350, lineno: 307}}, 44 | &Op{pos: pos{n: 1, p: 361, lineno: 308}}, 45 | }, 46 | want: []byte{ 47 | 6, 1, 48 | 44, 5, 49 | 255, 0, 50 | 45, 255, 51 | 0, 45, 52 | 11, 1}, 53 | }, 54 | } { 55 | got := test.instrs.Lnotab() 56 | if !bytes.Equal(test.want, got) { 57 | t.Errorf("%d: want %d got %d", i, test.want, got) 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /py/none.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // None objects 6 | 7 | package py 8 | 9 | type NoneType struct{} 10 | 11 | var ( 12 | NoneTypeType = NewType("NoneType", "") 13 | // And the ubiquitous 14 | None = NoneType(struct{}{}) 15 | ) 16 | 17 | // Type of this object 18 | func (s NoneType) Type() *Type { 19 | return NoneTypeType 20 | } 21 | 22 | func (a NoneType) M__bool__() (Object, error) { 23 | return False, nil 24 | } 25 | 26 | func (a NoneType) M__str__() (Object, error) { 27 | return a.M__repr__() 28 | } 29 | 30 | func (a NoneType) M__repr__() (Object, error) { 31 | return String("None"), nil 32 | } 33 | 34 | // Convert an Object to an NoneType 35 | // 36 | // Returns ok as to whether the conversion worked or not 37 | func convertToNoneType(other Object) (NoneType, bool) { 38 | switch b := other.(type) { 39 | case NoneType: 40 | return b, true 41 | } 42 | return None, false 43 | } 44 | 45 | func (a NoneType) M__eq__(other Object) (Object, error) { 46 | if _, ok := convertToNoneType(other); ok { 47 | return True, nil 48 | } 49 | return False, nil 50 | } 51 | 52 | func (a NoneType) M__ne__(other Object) (Object, error) { 53 | if _, ok := convertToNoneType(other); ok { 54 | return False, nil 55 | } 56 | return True, nil 57 | } 58 | 59 | // Check interface is satisfied 60 | var _ I__bool__ = None 61 | var _ I__str__ = None 62 | var _ I__repr__ = None 63 | var _ I__eq__ = None 64 | var _ I__ne__ = None 65 | -------------------------------------------------------------------------------- /py/iterator.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Iterator objects 6 | 7 | package py 8 | 9 | // A python Iterator object 10 | type Iterator struct { 11 | Pos int 12 | Seq Object 13 | } 14 | 15 | var IteratorType = NewType("iterator", "iterator type") 16 | 17 | // Type of this object 18 | func (o *Iterator) Type() *Type { 19 | return IteratorType 20 | } 21 | 22 | // Define a new iterator 23 | func NewIterator(Seq Object) *Iterator { 24 | m := &Iterator{ 25 | Pos: 0, 26 | Seq: Seq, 27 | } 28 | return m 29 | } 30 | 31 | func (it *Iterator) M__iter__() (Object, error) { 32 | return it, nil 33 | } 34 | 35 | // Get next one from the iteration 36 | func (it *Iterator) M__next__() (res Object, err error) { 37 | if tuple, ok := it.Seq.(Tuple); ok { 38 | if it.Pos >= len(tuple) { 39 | return nil, StopIteration 40 | } 41 | res = tuple[it.Pos] 42 | it.Pos++ 43 | return res, nil 44 | } 45 | index := Int(it.Pos) 46 | if I, ok := it.Seq.(I__getitem__); ok { 47 | res, err = I.M__getitem__(index) 48 | } else if res, ok, err = TypeCall1(it.Seq, "__getitem__", index); !ok { 49 | return nil, ExceptionNewf(TypeError, "'%s' object is not iterable", it.Type().Name) 50 | } 51 | if err != nil { 52 | if IsException(IndexError, err) { 53 | return nil, StopIteration 54 | } 55 | return nil, err 56 | } 57 | it.Pos++ 58 | return res, nil 59 | } 60 | 61 | // Check interface is satisfied 62 | var _ I_iterator = (*Iterator)(nil) 63 | -------------------------------------------------------------------------------- /ast/dump_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ast 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/go-python/gpython/py" 11 | ) 12 | 13 | func TestDump(t *testing.T) { 14 | for _, test := range []struct { 15 | in Ast 16 | out string 17 | }{ 18 | {nil, ``}, 19 | {&Pass{}, `Pass()`}, 20 | {&Str{S: py.String("potato")}, `Str(s='potato')`}, 21 | {&Str{S: py.String("potato")}, `Str(s='potato')`}, 22 | {&Bytes{S: py.Bytes("potato")}, `Bytes(s=b'potato')`}, 23 | {&BinOp{Left: &Str{S: py.String("one")}, Op: Add, Right: &Str{S: py.String("two")}}, 24 | `BinOp(left=Str(s='one'), op=Add(), right=Str(s='two'))`}, 25 | {&Module{}, `Module(body=[])`}, 26 | {&Module{Body: []Stmt{&Pass{}}}, `Module(body=[Pass()])`}, 27 | {&Module{Body: []Stmt{&ExprStmt{Value: &Tuple{}}}}, `Module(body=[Expr(value=Tuple(elts=[], ctx=UnknownExprContext(0)))])`}, 28 | {&NameConstant{Value: py.True}, `NameConstant(value=True)`}, 29 | {&Name{Id: Identifier("hello"), Ctx: Load}, `Name(id='hello', ctx=Load())`}, 30 | {&ListComp{Elt: &Str{S: py.String("potato")}, Generators: []Comprehension{{ 31 | Target: &Name{Id: Identifier("hello"), Ctx: Load}, 32 | }}}, `ListComp(elt=Str(s='potato'), generators=[comprehension(target=Name(id='hello', ctx=Load()), iter=None, ifs=[])])`}, 33 | } { 34 | out := Dump(test.in) 35 | if out != test.out { 36 | t.Errorf("Dump(%#v) got %q expected %q", test.in, out, test.out) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright ©2015 The go-python Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | * Redistributions of source code must retain the above copyright 6 | notice, this list of conditions and the following disclaimer. 7 | * Redistributions in binary form must reproduce the above copyright 8 | notice, this list of conditions and the following disclaimer in the 9 | documentation and/or other materials provided with the distribution. 10 | * Neither the name of the gonum project nor the names of its authors and 11 | contributors may be used to endorse or promote products derived from this 12 | software without specific prior written permission. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /examples/embedding/lib/mylib.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | import mylib_go as _go 6 | 7 | PY_VERSION = _go.PY_VERSION 8 | 9 | 10 | print(''' 11 | ========================================================== 12 | %s 13 | ========================================================== 14 | ''' % (PY_VERSION, )) 15 | 16 | 17 | def Stop(location, num_nights = 2): 18 | return _go.VacationStop_new(location, num_nights) 19 | 20 | 21 | class Vacation: 22 | 23 | def __init__(self, tripName, *stops): 24 | self._v, self._libVers = _go.Vacation_new() 25 | self.tripName = tripName 26 | self.AddStops(*stops) 27 | 28 | def __str__(self): 29 | return "%s, %d stop(s)" % (self.tripName, self.NumStops()) 30 | 31 | def NumStops(self): 32 | return self._v.num_stops() 33 | 34 | def GetStop(self, stop_num): 35 | return self._v.get_stop(stop_num) 36 | 37 | def AddStops(self, *stops): 38 | self._v.add_stops(stops) 39 | 40 | def PrintItinerary(self): 41 | print(self.tripName, "itinerary:") 42 | i = 1 43 | while 1: 44 | 45 | try: 46 | stop = self.GetStop(i) 47 | except IndexError: 48 | break 49 | 50 | print(" Stop %d: %s" % (i, str(stop))) 51 | i += 1 52 | 53 | print("### Made with %s " % self._libVers) 54 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= 2 | github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 3 | github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c h1:16eHWuMGvCjSfgRJKqIzapE78onvvTbdi1rMkU00lZw= 4 | github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 5 | github.com/gopherjs/gopherwasm v1.1.0 h1:fA2uLoctU5+T3OhOn2vYP0DVT6pxc7xhTlBB1paATqQ= 6 | github.com/gopherjs/gopherwasm v1.1.0/go.mod h1:SkZ8z7CWBz5VXbhJel8TxCmAcsQqzgWGR/8nMhyhZSI= 7 | github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= 8 | github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= 9 | github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 10 | github.com/peterh/liner v1.2.2 h1:aJ4AOodmL+JxOZZEL2u9iJf8omNRpqHc/EbrK+3mAXw= 11 | github.com/peterh/liner v1.2.2/go.mod h1:xFwJyiKIXJZUKItq5dGHZSTBRAuG/CpeNpWLyiNRNwI= 12 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 13 | github.com/rivo/uniseg v0.3.4 h1:3Z3Eu6FGHZWSfNKJTOUiPatWwfc7DzJRU04jFUqJODw= 14 | github.com/rivo/uniseg v0.3.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 15 | golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 16 | golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2 h1:fqTvyMIIj+HRzMmnzr9NtpHP6uVpvB5fkHcgPDC4nu8= 17 | golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 18 | -------------------------------------------------------------------------------- /vm/tests/builtin.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | doc="eval" 6 | assert eval("1+2") == 3 7 | glob ={'a':1} 8 | assert eval("a+2", glob) == 3 9 | loc ={'b':2} 10 | assert eval("a+b", glob, loc) == 3 11 | co = compile("a+b+1", "s", "eval") 12 | assert eval(co, glob, loc) == 4 13 | assert eval(b"2+3") == 5 14 | 15 | try: 16 | eval(()) 17 | except TypeError as e: 18 | pass 19 | else: 20 | assert False, "SyntaxError not raised" 21 | 22 | try: 23 | eval("a = 26") 24 | except SyntaxError as e: 25 | pass 26 | else: 27 | assert False, "SyntaxError not raised" 28 | 29 | try: 30 | eval(1,2,3,4) 31 | except TypeError as e: 32 | pass 33 | else: 34 | assert False, "TypeError not raised" 35 | 36 | try: 37 | eval("1", object()) 38 | except TypeError as e: 39 | pass 40 | else: 41 | assert False, "TypeError not raised" 42 | 43 | try: 44 | eval("1", {}, object()) 45 | except TypeError as e: 46 | pass 47 | else: 48 | assert False, "TypeError not raised" 49 | 50 | doc="exec" 51 | glob = {"a":100} 52 | assert exec("b = a+100", glob) == None 53 | assert glob["b"] == 200 54 | loc = {"c":23} 55 | assert exec("d = a+b+c", glob, loc) == None 56 | assert loc["d"] == 323 57 | co = compile("d = a+b+c+1", "s", "exec") 58 | assert eval(co, glob, loc) == None 59 | assert loc["d"] == 324 60 | 61 | try: 62 | exec("if") 63 | except SyntaxError as e: 64 | pass 65 | else: 66 | assert False, "SyntaxError not raised" 67 | 68 | doc="finished" 69 | -------------------------------------------------------------------------------- /vm/tests/loops.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | doc="While" 6 | a = 1 7 | while a < 10: 8 | a += 1 9 | assert a == 10 10 | 11 | doc="While else" 12 | a = 1 13 | ok = False 14 | while a < 10: 15 | a += 1 16 | else: 17 | ok = True 18 | assert a == 10 19 | assert ok 20 | 21 | doc="While break" 22 | a = 1 23 | ok = True 24 | while True: 25 | if a >= 10: 26 | break 27 | a += 1 28 | else: 29 | ok = False 30 | assert a == 10 31 | assert ok 32 | 33 | doc="While continue" 34 | a = 1 35 | while a < 10: 36 | if a == 5: 37 | a += 1000 38 | continue 39 | a += 1 40 | assert a == 1005 41 | 42 | doc="For" 43 | a = 0 44 | for i in (1,2,3,4,5): 45 | a += i 46 | assert a == 15 47 | 48 | doc="For else" 49 | a = 0 50 | ok = False 51 | for i in (1,2,3,4,5): 52 | a += i 53 | else: 54 | ok = True 55 | assert a == 15 56 | assert ok 57 | 58 | doc="For break" 59 | a = 0 60 | ok = True 61 | for i in (1,2,3,4,5): 62 | if i >= 3: 63 | break 64 | a += i 65 | else: 66 | ok = False 67 | assert a == 3 68 | assert ok 69 | 70 | doc="For continue" 71 | a = 0 72 | for i in (1,2,3,4,5): 73 | if i == 3: 74 | continue 75 | a += i 76 | assert a == 12 77 | 78 | doc="For continue in try/finally" 79 | ok = False 80 | a = 0 81 | for i in (1,2,3,4,5): 82 | if i == 3: 83 | try: 84 | continue 85 | finally: 86 | ok = True 87 | a += i 88 | assert a == 12 89 | assert ok 90 | 91 | doc="finished" 92 | -------------------------------------------------------------------------------- /main_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "bytes" 9 | "flag" 10 | "os" 11 | "os/exec" 12 | "path/filepath" 13 | "testing" 14 | ) 15 | 16 | var regen = flag.Bool("regen", false, "regenerate golden files") 17 | 18 | func TestGPython(t *testing.T) { 19 | 20 | tmp, err := os.MkdirTemp("", "go-python-") 21 | if err != nil { 22 | t.Fatal(err) 23 | } 24 | defer os.RemoveAll(tmp) 25 | 26 | exe := filepath.Join(tmp, "out.exe") 27 | cmd := exec.Command("go", "build", "-o", exe, ".") 28 | cmd.Stdout = os.Stdout 29 | cmd.Stderr = os.Stderr 30 | err = cmd.Run() 31 | if err != nil { 32 | t.Fatalf("failed to compile embedding example: %+v", err) 33 | } 34 | 35 | got, err := exec.Command(exe, "testdata/hello.py").CombinedOutput() 36 | if err != nil { 37 | t.Fatalf("could not run gpython:\n%s\nerr: %+v", got, err) 38 | } 39 | 40 | const fname = "testdata/hello_golden.txt" 41 | 42 | flag.Parse() 43 | if *regen { 44 | err = os.WriteFile(fname, got, 0644) 45 | if err != nil { 46 | t.Fatalf("could not write golden file: %+v", err) 47 | } 48 | } 49 | 50 | want, err := os.ReadFile(fname) 51 | want = bytes.ReplaceAll(want, []byte("\r\n"), []byte("\n")) 52 | if err != nil { 53 | t.Fatalf("could not read golden file: %+v", err) 54 | } 55 | if !bytes.Equal(got, want) { 56 | t.Fatalf("stdout differ:\ngot:\n%s\nwant:\n%s\n", got, want) 57 | } 58 | } 59 | 60 | func TestRunFile(t *testing.T) { 61 | xmain([]string{"./testdata/hello.py"}) 62 | } 63 | -------------------------------------------------------------------------------- /py/map.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package py 6 | 7 | // A python Map object 8 | type Map struct { 9 | iters Tuple 10 | fun Object 11 | } 12 | 13 | var MapType = NewTypeX("filter", `map(func, *iterables) --> map object 14 | 15 | Make an iterator that computes the function using arguments from 16 | each of the iterables. Stops when the shortest iterable is exhausted.`, 17 | MapTypeNew, nil) 18 | 19 | // Type of this object 20 | func (m *Map) Type() *Type { 21 | return FilterType 22 | } 23 | 24 | // MapType 25 | func MapTypeNew(metatype *Type, args Tuple, kwargs StringDict) (res Object, err error) { 26 | numargs := len(args) 27 | if numargs < 2 { 28 | return nil, ExceptionNewf(TypeError, "map() must have at least two arguments.") 29 | } 30 | iters := make(Tuple, numargs-1) 31 | for i := 1; i < numargs; i++ { 32 | iters[i-1], err = Iter(args[i]) 33 | if err != nil { 34 | return nil, err 35 | } 36 | } 37 | return &Map{iters: iters, fun: args[0]}, nil 38 | } 39 | 40 | func (m *Map) M__iter__() (Object, error) { 41 | return m, nil 42 | } 43 | 44 | func (m *Map) M__next__() (Object, error) { 45 | numargs := len(m.iters) 46 | argtuple := make(Tuple, numargs) 47 | 48 | for i := 0; i < numargs; i++ { 49 | val, err := Next(m.iters[i]) 50 | if err != nil { 51 | return nil, err 52 | } 53 | argtuple[i] = val 54 | } 55 | return Call(m.fun, argtuple, nil) 56 | } 57 | 58 | // Check interface is satisfied 59 | var _ I__iter__ = (*Map)(nil) 60 | var _ I__next__ = (*Map)(nil) 61 | -------------------------------------------------------------------------------- /py/object.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Functions to operate on any object 6 | 7 | package py 8 | 9 | import ( 10 | "fmt" 11 | ) 12 | 13 | // Gets the attribute attr from object or returns nil 14 | func ObjectGetAttr(o Object, attr string) Object { 15 | // FIXME 16 | return nil 17 | } 18 | 19 | // Gets the repr for an object 20 | func ObjectRepr(o Object) Object { 21 | // FIXME 22 | return String(fmt.Sprintf("<%s %v>", o.Type().Name, o)) 23 | } 24 | 25 | // Return whether the object is True or not 26 | func ObjectIsTrue(o Object) (cmp bool, err error) { 27 | switch o { 28 | case True: 29 | return true, nil 30 | case False: 31 | return false, nil 32 | case None: 33 | return false, nil 34 | } 35 | 36 | var res Object 37 | switch t := o.(type) { 38 | case I__bool__: 39 | res, err = t.M__bool__() 40 | case I__len__: 41 | res, err = t.M__len__() 42 | case *Type: 43 | var ok bool 44 | if res, ok, err = TypeCall0(o, "__bool__"); ok { 45 | break 46 | } 47 | if res, ok, err = TypeCall0(o, "__len__"); ok { 48 | break 49 | } 50 | _ = ok // pass static-check 51 | } 52 | if err != nil { 53 | return false, err 54 | } 55 | 56 | switch t := res.(type) { 57 | case Bool: 58 | return t == True, nil 59 | case Int: 60 | return t > 0, nil 61 | } 62 | return true, nil 63 | } 64 | 65 | // Return whether the object is a sequence 66 | func ObjectIsSequence(o Object) bool { 67 | switch t := o.(type) { 68 | case I__getitem__: 69 | return true 70 | case *Type: 71 | if t.GetAttrOrNil("__getitem__") != nil { 72 | return true 73 | } 74 | } 75 | return false 76 | } 77 | -------------------------------------------------------------------------------- /stdlib/glob/glob.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package glob provides the implementation of the python's 'glob' module. 6 | package glob 7 | 8 | import ( 9 | "path/filepath" 10 | 11 | "github.com/go-python/gpython/py" 12 | ) 13 | 14 | func init() { 15 | py.RegisterModule(&py.ModuleImpl{ 16 | Info: py.ModuleInfo{ 17 | Name: "glob", 18 | Doc: "Filename globbing utility.", 19 | }, 20 | Methods: []*py.Method{ 21 | py.MustNewMethod("glob", glob, 0, glob_doc), 22 | }, 23 | }) 24 | } 25 | 26 | const glob_doc = `Return a list of paths matching a pathname pattern. 27 | The pattern may contain simple shell-style wildcards a la 28 | fnmatch. However, unlike fnmatch, filenames starting with a 29 | dot are special cases that are not matched by '*' and '?' 30 | patterns.` 31 | 32 | func glob(self py.Object, args py.Tuple) (py.Object, error) { 33 | var ( 34 | pypathname py.Object 35 | ) 36 | err := py.ParseTuple(args, "s*:glob", &pypathname) 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | var ( 42 | pathname string 43 | cnv func(v string) py.Object 44 | ) 45 | switch n := pypathname.(type) { 46 | case py.String: 47 | pathname = string(n) 48 | cnv = func(v string) py.Object { return py.String(v) } 49 | case py.Bytes: 50 | pathname = string(n) 51 | cnv = func(v string) py.Object { return py.Bytes(v) } 52 | } 53 | matches, err := filepath.Glob(pathname) 54 | if err != nil { 55 | return nil, err 56 | } 57 | 58 | lst := py.List{Items: make([]py.Object, len(matches))} 59 | for i, v := range matches { 60 | lst.Items[i] = cnv(v) 61 | } 62 | 63 | return &lst, nil 64 | } 65 | -------------------------------------------------------------------------------- /py/tests/map.py: -------------------------------------------------------------------------------- 1 | # test_builtin.py:BuiltinTest.test_map() 2 | from libtest import assertRaises 3 | 4 | doc="map" 5 | class Squares: 6 | def __init__(self, max): 7 | self.max = max 8 | self.sofar = [] 9 | 10 | def __len__(self): return len(self.sofar) 11 | 12 | def __getitem__(self, i): 13 | if not 0 <= i < self.max: raise IndexError 14 | n = len(self.sofar) 15 | while n <= i: 16 | self.sofar.append(n*n) 17 | n += 1 18 | return self.sofar[i] 19 | 20 | assert list(map(lambda x: x*x, range(1,4))) == [1, 4, 9] 21 | try: 22 | from math import sqrt 23 | except ImportError: 24 | def sqrt(x): 25 | return pow(x, 0.5) 26 | assert list(map(lambda x: list(map(sqrt, x)), [[16, 4], [81, 9]])) == [[4.0, 2.0], [9.0, 3.0]] 27 | assert list(map(lambda x, y: x+y, [1,3,2], [9,1,4])) == [10, 4, 6] 28 | 29 | def plus(*v): 30 | accu = 0 31 | for i in v: accu = accu + i 32 | return accu 33 | assert list(map(plus, [1, 3, 7])) == [1, 3, 7] 34 | assert list(map(plus, [1, 3, 7], [4, 9, 2])) == [1+4, 3+9, 7+2] 35 | assert list(map(plus, [1, 3, 7], [4, 9, 2], [1, 1, 0])) == [1+4+1, 3+9+1, 7+2+0] 36 | assert list(map(int, Squares(10))) == [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 37 | def Max(a, b): 38 | if a is None: 39 | return b 40 | if b is None: 41 | return a 42 | return max(a, b) 43 | assert list(map(Max, Squares(3), Squares(2))) == [0, 1] 44 | assertRaises(TypeError, map) 45 | assertRaises(TypeError, map, lambda x: x, 42) 46 | class BadSeq: 47 | def __iter__(self): 48 | raise ValueError 49 | yield None 50 | assertRaises(ValueError, list, map(lambda x: x, BadSeq())) 51 | def badfunc(x): 52 | raise RuntimeError 53 | assertRaises(RuntimeError, list, map(badfunc, range(5))) 54 | doc="finished" 55 | -------------------------------------------------------------------------------- /parser/grammar_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package parser 6 | 7 | //go:generate ./make_grammar_test.py 8 | 9 | import ( 10 | "flag" 11 | "testing" 12 | 13 | "github.com/go-python/gpython/ast" 14 | "github.com/go-python/gpython/py" 15 | ) 16 | 17 | var debugLevel = flag.Int("debugLevel", 0, "Debug level 0-4") 18 | 19 | // FIXME test pos is correct 20 | 21 | func TestGrammar(t *testing.T) { 22 | SetDebug(*debugLevel) 23 | for _, test := range grammarTestData { 24 | Ast, err := ParseString(test.in, py.CompileMode(test.mode)) 25 | if err != nil { 26 | if test.exceptionType == nil { 27 | t.Errorf("%s: Got exception %v when not expecting one", test.in, err) 28 | return 29 | } else if exc, ok := err.(*py.Exception); !ok { 30 | t.Errorf("%s: Got non python exception %T %v", test.in, err, err) 31 | return 32 | } else if exc.Type() != test.exceptionType { 33 | t.Errorf("%s: want exception type %v got %v", test.in, test.exceptionType, exc.Type()) 34 | return 35 | } else if exc.Type() != test.exceptionType { 36 | t.Errorf("%s: want exception type %v got %v", test.in, test.exceptionType, exc.Type()) 37 | return 38 | } else { 39 | msg := string(exc.Args.(py.Tuple)[0].(py.String)) 40 | if msg != test.errString { 41 | t.Errorf("%s: want exception text %q got %q", test.in, test.errString, msg) 42 | } 43 | } 44 | } else { 45 | if test.exceptionType != nil { 46 | t.Errorf("%s: expecting exception %q", test.in, test.errString) 47 | } else { 48 | out := ast.Dump(Ast) 49 | if out != test.out { 50 | t.Errorf("Parse(%q)\nwant> %q\n got> %q\n", test.in, test.out, out) 51 | } 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /py/zip.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package py 6 | 7 | // A python Zip object 8 | type Zip struct { 9 | itTuple Tuple 10 | size int 11 | } 12 | 13 | // // A python ZipIterator iterator 14 | // type ZipIterator struct { 15 | // zip Zip 16 | // } 17 | 18 | var ZipType = NewTypeX("zip", `zip(iter1 [,iter2 [...]]) --> zip object 19 | 20 | Return a zip object whose .__next__() method returns a tuple where 21 | the i-th element comes from the i-th iterable argument. The .__next__() 22 | method continues until the shortest iterable in the argument sequence 23 | is exhausted and then it raises StopIteration.`, 24 | ZipTypeNew, nil) 25 | 26 | // Type of this object 27 | func (z *Zip) Type() *Type { 28 | return ZipType 29 | } 30 | 31 | // ZipTypeNew 32 | func ZipTypeNew(metatype *Type, args Tuple, kwargs StringDict) (Object, error) { 33 | tupleSize := len(args) 34 | itTuple := make(Tuple, tupleSize) 35 | for i := 0; i < tupleSize; i++ { 36 | item := args[i] 37 | iter, err := Iter(item) 38 | if err != nil { 39 | return nil, ExceptionNewf(TypeError, "zip argument #%d must support iteration", i+1) 40 | } 41 | itTuple[i] = iter 42 | } 43 | 44 | return &Zip{itTuple: itTuple, size: tupleSize}, nil 45 | } 46 | 47 | // Zip iterator 48 | func (z *Zip) M__iter__() (Object, error) { 49 | return z, nil 50 | } 51 | 52 | func (z *Zip) M__next__() (Object, error) { 53 | result := make(Tuple, z.size) 54 | for i := 0; i < z.size; i++ { 55 | value, err := Next(z.itTuple[i]) 56 | if err != nil { 57 | return nil, err 58 | } 59 | result[i] = value 60 | } 61 | return result, nil 62 | } 63 | 64 | // Check interface is satisfied 65 | var _ I__iter__ = (*Zip)(nil) 66 | var _ I__next__ = (*Zip)(nil) 67 | -------------------------------------------------------------------------------- /parser/testparser/testparser.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "flag" 9 | "fmt" 10 | "io" 11 | "log" 12 | "os" 13 | 14 | "github.com/go-python/gpython/compile" 15 | "github.com/go-python/gpython/parser" 16 | ) 17 | 18 | var ( 19 | lexFile = flag.Bool("l", false, "Lex the file only") 20 | compileFile = flag.Bool("c", false, "Lex, Parse and compile the file") 21 | debugLevel = flag.Int("d", 0, "Debug level 0-4") 22 | ) 23 | 24 | func main() { 25 | flag.Parse() 26 | parser.SetDebug(*debugLevel) 27 | if len(flag.Args()) == 0 { 28 | log.Printf("Need files to parse") 29 | os.Exit(1) 30 | } 31 | for _, path := range flag.Args() { 32 | if *lexFile { 33 | fmt.Printf("Lexing %q\n", path) 34 | } else if *compileFile { 35 | fmt.Printf("Compiling %q\n", path) 36 | } else { 37 | fmt.Printf("Parsing %q\n", path) 38 | } 39 | in, err := os.Open(path) 40 | if err != nil { 41 | log.Fatal(err) 42 | } 43 | if *debugLevel > 0 { 44 | fmt.Printf("-----------------\n") 45 | } 46 | if *lexFile { 47 | _, err = parser.Lex(in, path, "exec") 48 | } else if *compileFile { 49 | var input []byte 50 | input, err = io.ReadAll(in) 51 | if err != nil { 52 | log.Fatalf("Failed to read %q: %v", path, err) 53 | } 54 | _, err = compile.Compile(string(input), path, "exec", 0, false) 55 | } else { 56 | _, err = parser.Parse(in, path, "exec") 57 | } 58 | if *debugLevel > 0 { 59 | fmt.Printf("-----------------\n") 60 | } 61 | closeErr := in.Close() 62 | if err != nil { 63 | log.Fatalf("Failed on %q: %v", path, err) 64 | } 65 | if closeErr != nil { 66 | log.Fatalf("Failed to close %q: %v", path, closeErr) 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /vm/tests/libtest.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | # Imitate the calling method of unittest 6 | 7 | def assertRaises(expecting, fn, *args, **kwargs): 8 | """Check the exception was raised - don't check the text""" 9 | try: 10 | fn(*args, **kwargs) 11 | except expecting as e: 12 | pass 13 | else: 14 | assert False, "%s not raised" % (expecting,) 15 | 16 | def assertEqual(first, second, msg=None): 17 | if msg: 18 | assert first == second, "%s not equal" % (msg,) 19 | else: 20 | assert first == second 21 | 22 | def assertIs(expr1, expr2, msg=None): 23 | if msg: 24 | assert expr1 is expr2, "%s is not None" % (msg,) 25 | else: 26 | assert expr1 is expr2 27 | 28 | def assertIsNone(obj, msg=None): 29 | if msg: 30 | assert obj is None, "%s is not None" % (msg,) 31 | else: 32 | assert obj is None 33 | 34 | def assertTrue(obj, msg=None): 35 | if msg: 36 | assert obj, "%s is not True" % (msg,) 37 | else: 38 | assert obj 39 | 40 | def assertRaisesText(expecting, text, fn, *args, **kwargs): 41 | """Check the exception with text in is raised""" 42 | try: 43 | fn(*args, **kwargs) 44 | except expecting as e: 45 | assert text in e.args[0], "'%s' not found in '%s'" % (text, e.args[0]) 46 | else: 47 | assert False, "%s not raised" % (expecting,) 48 | 49 | def assertTypedEqual(actual, expect, msg=None): 50 | assertEqual(actual, expect, msg) 51 | def recurse(actual, expect): 52 | if isinstance(expect, (tuple, list)): 53 | for x, y in zip(actual, expect): 54 | recurse(x, y) 55 | else: 56 | assertIs(type(actual), type(expect)) 57 | recurse(actual, expect) 58 | -------------------------------------------------------------------------------- /py/filter.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package py 6 | 7 | // A python Filter object 8 | type Filter struct { 9 | it Object 10 | fun Object 11 | } 12 | 13 | var FilterType = NewTypeX("filter", `filter(function or None, iterable) --> filter object 14 | 15 | Return an iterator yielding those items of iterable for which function(item) 16 | is true. If function is None, return the items that are true.`, 17 | FilterTypeNew, nil) 18 | 19 | // Type of this object 20 | func (f *Filter) Type() *Type { 21 | return FilterType 22 | } 23 | 24 | // FilterTypeNew 25 | func FilterTypeNew(metatype *Type, args Tuple, kwargs StringDict) (res Object, err error) { 26 | var fun, seq Object 27 | var it Object 28 | err = UnpackTuple(args, kwargs, "filter", 2, 2, &fun, &seq) 29 | if err != nil { 30 | return nil, err 31 | } 32 | it, err = Iter(seq) 33 | if err != nil { 34 | return nil, err 35 | } 36 | return &Filter{it: it, fun: fun}, nil 37 | } 38 | 39 | func (f *Filter) M__iter__() (Object, error) { 40 | return f, nil 41 | } 42 | 43 | func (f *Filter) M__next__() (Object, error) { 44 | var ok bool 45 | for { 46 | item, err := Next(f.it) 47 | if err != nil { 48 | return nil, err 49 | } 50 | // if (lz->func == Py_None || lz->func == (PyObject *)&PyBool_Type) 51 | if _, _ok := f.fun.(Bool); _ok || f.fun == None { 52 | ok, err = ObjectIsTrue(item) 53 | } else { 54 | var good Object 55 | good, err = Call(f.fun, Tuple{item}, nil) 56 | if err != nil { 57 | return nil, err 58 | } 59 | ok, err = ObjectIsTrue(good) 60 | } 61 | if ok { 62 | return item, nil 63 | } 64 | if err != nil { 65 | return nil, err 66 | } 67 | } 68 | } 69 | 70 | // Check interface is satisfied 71 | var _ I__iter__ = (*Filter)(nil) 72 | var _ I__next__ = (*Filter)(nil) 73 | -------------------------------------------------------------------------------- /repl/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 52 | 53 | 54 |
55 |
Loading...
56 | 61 |
62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /py/tests/lib_gen_int_tests.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | # Generate some tests 6 | unops = "- + abs ~ float complex int" 7 | augops = "+ - * / // % & | ^" 8 | binops = augops + " < <= == != > >=" 9 | # powmod 10 | # divmod 11 | # lsl 12 | # lsr 13 | # round 14 | # ** 15 | # **= 16 | 17 | import random 18 | 19 | def big(): 20 | return random.randint(-1000000000000000000000000000000, 1000000000000000000000000000000) 21 | 22 | def small(): 23 | return random.randint(-1<<63, (1<<63)-1) 24 | 25 | def unop(op, a): 26 | if len(op) == 1: 27 | expr = "%s%s" % (op, a) 28 | else: 29 | expr = "%s(%s)" % (op, a) 30 | r = eval(expr) 31 | print("assert (%s) == %s" % (expr, r)) 32 | 33 | for op in unops.split(): 34 | print("\ndoc='unop %s'" % op) 35 | for i in range(2): 36 | a = small() 37 | unop(op, a) 38 | a = big() 39 | unop(op, a) 40 | 41 | def binop(op, a, b): 42 | expr = "%s%s%s" % (a, op, b) 43 | r = eval(expr) 44 | print("assert (%s) == %s" % (expr, r)) 45 | 46 | for op in binops.split(): 47 | print("\ndoc='binop %s'" % op) 48 | a = small() 49 | b = small() 50 | binop(op, a, b) 51 | a = big() 52 | b = small() 53 | binop(op, a, b) 54 | a = small() 55 | b = big() 56 | binop(op, a, b) 57 | a = big() 58 | b = big() 59 | binop(op, a, b) 60 | 61 | def augop(op, a, b): 62 | expr = "%s%s%s" % (a, op, b) 63 | r = eval(expr) 64 | print("a = %s\na %s= %s\nassert a == %s" % (a, op, b, r)) 65 | 66 | for op in augops.split(): 67 | print("\ndoc='augop %s'" % op) 68 | a = small() 69 | b = small() 70 | augop(op, a, b) 71 | a = big() 72 | b = small() 73 | augop(op, a, b) 74 | a = small() 75 | b = big() 76 | augop(op, a, b) 77 | a = big() 78 | b = big() 79 | augop(op, a, b) 80 | 81 | -------------------------------------------------------------------------------- /vm/tests/lists.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | L = [1,2,3] 6 | T = (1,2,3) 7 | assert L == [1,2,3] 8 | assert L != [1,4,3] 9 | 10 | doc="UNPACK_SEQUENCE" 11 | a, b, c = L 12 | assert a == 1 13 | assert b == 2 14 | assert c == 3 15 | 16 | a, b, c = T 17 | assert a == 1 18 | assert b == 2 19 | assert c == 3 20 | 21 | a, b, c = range(3) 22 | assert a == 0 23 | assert b == 1 24 | assert c == 2 25 | 26 | ok = False 27 | try: 28 | a, b = L 29 | except ValueError: 30 | ok = True 31 | assert ok 32 | 33 | doc="UNPACK_EX" 34 | a, *b = L 35 | assert a == 1 36 | assert b == [2,3] 37 | 38 | doc="SETITEM" 39 | LL = [1,2,3] 40 | LL[1] = 17 41 | assert LL == [1,17,3] 42 | 43 | L=[1,2,3] 44 | L[:] = [4,5,6,7] 45 | assert L == [4,5,6,7] 46 | 47 | L=[1,2,3] 48 | L[3:3] = [4,5,6] 49 | assert L == [1,2,3,4,5,6] 50 | 51 | L=[1,2,3,4] 52 | L[1:3] = [5,6,7] 53 | assert L == [1,5,6,7,4] 54 | 55 | L=[1,2,3,4] 56 | L[:2] = [5,6,7] 57 | assert L == [5,6,7,3,4] 58 | 59 | L=[1,2,3,4] 60 | L[2:] = [5,6,7] 61 | assert L == [1,2,5,6,7] 62 | 63 | L=[1,2,3,4] 64 | L[::2] = [7,8] 65 | assert L == [7, 2, 8, 4] 66 | 67 | doc="GETITEM" 68 | assert LL[0] == 1 69 | assert LL[1] == 17 70 | assert LL[2] == 3 71 | 72 | L=[1,2,3,4,5,6] 73 | assert L[:] == [1,2,3,4,5,6] 74 | assert L[:3] == [1,2,3] 75 | assert L[3:] == [4,5,6] 76 | assert L[1:3] == [2,3] 77 | assert L[1:5:2] == [2,4] 78 | 79 | doc="DELITEM" 80 | del LL[1] 81 | assert LL == [1,3] 82 | 83 | L=[1,2,3,4,5,6] 84 | del L[:3] 85 | assert L == [4,5,6] 86 | 87 | L=[1,2,3,4,5,6] 88 | del L[3:] 89 | assert L == [1,2,3] 90 | 91 | L=[1,2,3,4,5,6] 92 | del L[1:5] 93 | assert L == [1,6] 94 | 95 | L=[1,2,3,4,5,6] 96 | del L[1:5:2] 97 | assert L == [1,3,5,6] 98 | 99 | L=[1,2,3,4,5,6] 100 | del L[:] 101 | assert L == [] 102 | 103 | doc="finished" 104 | -------------------------------------------------------------------------------- /py/staticmethod.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // StaticMethod objects 6 | 7 | package py 8 | 9 | var StaticMethodType = ObjectType.NewType("staticmethod", 10 | `staticmethod(function) -> method 11 | 12 | Convert a function to be a static method. 13 | 14 | A static method does not receive an implicit first argument. 15 | To declare a static method, use this idiom: 16 | 17 | class C: 18 | def f(arg1, arg2, ...): ... 19 | f = staticmethod(f) 20 | 21 | It can be called either on the class (e.g. C.f()) or on an instance 22 | (e.g. C().f()). The instance is ignored except for its class. 23 | 24 | Static methods in Python are similar to those found in Java or C++. 25 | For a more advanced concept, see the classmethod builtin.`, StaticMethodNew, nil) 26 | 27 | type StaticMethod struct { 28 | Callable Object 29 | Dict StringDict 30 | } 31 | 32 | // Type of this StaticMethod object 33 | func (o StaticMethod) Type() *Type { 34 | return StaticMethodType 35 | } 36 | 37 | // Get the Dict 38 | func (c *StaticMethod) GetDict() StringDict { 39 | return c.Dict 40 | } 41 | 42 | // StaticMethodNew 43 | func StaticMethodNew(metatype *Type, args Tuple, kwargs StringDict) (res Object, err error) { 44 | c := &StaticMethod{ 45 | Dict: make(StringDict), 46 | } 47 | err = UnpackTuple(args, kwargs, "staticmethod", 1, 1, &c.Callable) 48 | if err != nil { 49 | return nil, err 50 | } 51 | return c, nil 52 | } 53 | 54 | // Read a staticmethod from a class - no bound method here 55 | func (c *StaticMethod) M__get__(instance, owner Object) (Object, error) { 56 | return c.Callable, nil 57 | } 58 | 59 | // Properties 60 | func init() { 61 | StaticMethodType.Dict["__func__"] = &Property{ 62 | Fget: func(self Object) (Object, error) { 63 | return self.(*StaticMethod).Callable, nil 64 | }, 65 | } 66 | } 67 | 68 | // Check interface is satisfied 69 | var _ IGetDict = (*StaticMethod)(nil) 70 | var _ I__get__ = (*StaticMethod)(nil) 71 | -------------------------------------------------------------------------------- /py/tests/filter.py: -------------------------------------------------------------------------------- 1 | # test_builtin.py:BuiltinTest.test_filter() 2 | from libtest import assertRaises 3 | 4 | doc="filter" 5 | class T0: 6 | def __bool__(self): 7 | return True 8 | class T1: 9 | def __len__(self): 10 | return 1 11 | class T2: 12 | def __bool__(self): 13 | return False 14 | class T3: 15 | pass 16 | t0, t1, t2, t3 = T0(), T1(), T2(), T3() 17 | assert list(filter(None, [t0, t1, t2, t3])) == [t0, t1, t3] 18 | assert list(filter(None, [1, [], 2, ''])) == [1, 2] 19 | 20 | class T3: 21 | def __len__(self): 22 | raise ValueError 23 | t3 = T3() 24 | assertRaises(ValueError, list, filter(None, [t3])) 25 | 26 | class Squares: 27 | def __init__(self, max): 28 | self.max = max 29 | self.sofar = [] 30 | 31 | def __len__(self): return len(self.sofar) 32 | 33 | def __getitem__(self, i): 34 | if not 0 <= i < self.max: raise IndexError 35 | n = len(self.sofar) 36 | while n <= i: 37 | self.sofar.append(n*n) 38 | n += 1 39 | return self.sofar[i] 40 | 41 | assert list(filter(lambda c: 'a' <= c <= 'z', 'Hello World')) == list('elloorld') 42 | assert list(filter(None, [1, 'hello', [], [3], '', None, 9, 0])) == [1, 'hello', [3], 9] 43 | assert list(filter(lambda x: x > 0, [1, -3, 9, 0, 2])) == [1, 9, 2] 44 | assert list(filter(None, Squares(10))) == [1, 4, 9, 16, 25, 36, 49, 64, 81] 45 | assert list(filter(lambda x: x%2, Squares(10))) == [1, 9, 25, 49, 81] 46 | def identity(item): 47 | return 1 48 | filter(identity, Squares(5)) 49 | assertRaises(TypeError, filter) 50 | class BadSeq(object): 51 | def __getitem__(self, index): 52 | if index<4: 53 | return 42 54 | raise ValueError 55 | assertRaises(ValueError, list, filter(lambda x: x, BadSeq())) 56 | def badfunc(): 57 | pass 58 | assertRaises(TypeError, list, filter(badfunc, range(5))) 59 | 60 | # test bltinmodule.c::filtertuple() 61 | assert list(filter(None, (1, 2))) == [1, 2] 62 | assert list(filter(lambda x: x>=3, (1, 2, 3, 4))) == [3, 4] 63 | assertRaises(TypeError, list, filter(42, (1, 2))) 64 | 65 | doc="finished" 66 | -------------------------------------------------------------------------------- /vm/tests/generators.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | doc="generator 1" 6 | def g1(): 7 | yield 1 8 | yield 2 9 | yield 3 10 | i = g1() 11 | assert next(i) == 1 12 | assert next(i) == 2 13 | assert next(i) == 3 14 | for _ in (1, 2): 15 | ok = False 16 | try: 17 | next(i) 18 | except StopIteration: 19 | ok = True 20 | assert ok, "StopIteration not raised" 21 | 22 | doc="generator 2" 23 | def g2(): 24 | for i in range(5): 25 | yield i 26 | assert tuple(g2()) == (0,1,2,3,4) 27 | 28 | doc="generator 3" 29 | ok = False 30 | try: 31 | list(ax for x in range(10)) 32 | except NameError: 33 | ok = True 34 | assert ok, "NameError not raised" 35 | 36 | doc="yield from" 37 | def g3(): 38 | yield "potato" 39 | yield from g1() 40 | yield "sausage" 41 | assert tuple(g3()) == ("potato",1,2,3,"sausage") 42 | 43 | doc="yield into" 44 | state = "not started" 45 | def echo(value=None): 46 | """Example from python docs""" 47 | global state 48 | state = "started" 49 | try: 50 | while True: 51 | try: 52 | value = (yield value) 53 | except Exception as e: 54 | value = e 55 | finally: 56 | # clean up when close is called 57 | state = "finally" 58 | 59 | assert state == "not started" 60 | generator = echo(1) 61 | 62 | assert state == "not started" 63 | 64 | assert next(generator) == 1 65 | assert state == "started" 66 | 67 | assert next(generator) == None 68 | assert state == "started" 69 | 70 | assert generator.send(2) == 2 71 | assert state == "started" 72 | 73 | assert generator.send(3) == 3 74 | assert state == "started" 75 | 76 | assert next(generator) == None 77 | assert state == "started" 78 | 79 | # FIXME not implemented 80 | # err = ValueError("potato") 81 | # e = generator.throw(ValueError, "potato") 82 | # assert isinstance(e, ValueError) 83 | # assert state == "started" 84 | 85 | # FIXME not implemented 86 | # generator.close() 87 | # assert state == "finally" 88 | 89 | 90 | doc="finished" 91 | -------------------------------------------------------------------------------- /stdlib/glob/testdata/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | import glob 6 | 7 | def norm(vs): 8 | if len(vs) == 0: 9 | return vs 10 | if type(vs[0]) == type(""): 11 | return normStr(vs) 12 | return normBytes(vs) 13 | 14 | def normStr(vs): 15 | from os import sep 16 | x = [] 17 | for v in vs: 18 | x.append(v.replace('/', sep)) 19 | return x 20 | 21 | def normBytes(vs): 22 | from os import sep 23 | x = [] 24 | for v in vs: 25 | x.append(v.replace(b'/', bytes(sep, encoding="utf-8"))) 26 | return x 27 | 28 | def assertEqual(x, y): 29 | xx = norm(x) 30 | yy = norm(y) 31 | assert xx == yy, "got: %s, want: %s" % (repr(x), repr(y)) 32 | 33 | 34 | ## test strings 35 | assertEqual(glob.glob('*'), ["glob.go", "glob_test.go", "testdata"]) 36 | assertEqual(glob.glob('*test*'), ["glob_test.go", "testdata"]) 37 | assertEqual(glob.glob('*/test*'), ["testdata/test.py", "testdata/test_golden.txt"]) 38 | assertEqual(glob.glob('*/test*_*'), ["testdata/test_golden.txt"]) 39 | assertEqual(glob.glob('*/t??t*_*'), ["testdata/test_golden.txt"]) 40 | assertEqual(glob.glob('*/t[e]?t*_*'), ["testdata/test_golden.txt"]) 41 | assertEqual(glob.glob('*/t[oe]?t*_*'), ["testdata/test_golden.txt"]) 42 | assertEqual(glob.glob('*/t[o]?t*_*'), []) 43 | 44 | ## FIXME(sbinet) 45 | ## assertEqual(glob.glob('*/t[!o]?t*_*'), ["testdata/test_golden.txt"]) 46 | 47 | ## test bytes 48 | assertEqual(glob.glob(b'*'), [b"glob.go", b"glob_test.go", b"testdata"]) 49 | assertEqual(glob.glob(b'*test*'), [b"glob_test.go", b"testdata"]) 50 | assertEqual(glob.glob(b'*/test*'), [b"testdata/test.py", b"testdata/test_golden.txt"]) 51 | assertEqual(glob.glob(b'*/test*_*'), [b"testdata/test_golden.txt"]) 52 | assertEqual(glob.glob(b'*/t??t*_*'), [b"testdata/test_golden.txt"]) 53 | assertEqual(glob.glob(b'*/t[e]?t*_*'), [b"testdata/test_golden.txt"]) 54 | assertEqual(glob.glob(b'*/t[oe]?t*_*'), [b"testdata/test_golden.txt"]) 55 | assertEqual(glob.glob(b'*/t[o]?t*_*'), []) 56 | 57 | ## FIXME(sbinet) 58 | ## assertEqual(glob.glob(b'*/t[!o]?t*_*'), [b"testdata/test_golden.txt"]) 59 | -------------------------------------------------------------------------------- /py/enumerate.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package py 6 | 7 | // A python Enumerate object 8 | type Enumerate struct { 9 | Iterable Object 10 | Start Int 11 | } 12 | 13 | // A python Enumerate iterator 14 | type EnumerateIterator struct { 15 | Enumerate 16 | Index Int 17 | } 18 | 19 | var EnumerateType = NewTypeX("enumerate", `enumerate(iterable, start=0) 20 | 21 | Return an enumerate object.`, 22 | EnumerateNew, nil) 23 | 24 | var EnumerateIteratorType = NewType("enumerate_iterator", `enumerate_iterator object`) 25 | 26 | // Type of this object 27 | func (e *Enumerate) Type() *Type { 28 | return EnumerateType 29 | } 30 | 31 | // Type of this object 32 | func (ei *EnumerateIterator) Type() *Type { 33 | return EnumerateIteratorType 34 | } 35 | 36 | // EnumerateTypeNew 37 | func EnumerateNew(metatype *Type, args Tuple, kwargs StringDict) (Object, error) { 38 | var iterable Object 39 | var start Object 40 | err := UnpackTuple(args, kwargs, "enumerate", 1, 2, &iterable, &start) 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | if start == nil { 46 | start = Int(0) 47 | } 48 | startIndex, err := Index(start) 49 | if err != nil { 50 | return nil, err 51 | } 52 | iter, err := Iter(iterable) 53 | if err != nil { 54 | return nil, err 55 | } 56 | 57 | return &Enumerate{Iterable: iter, Start: startIndex}, nil 58 | } 59 | 60 | // Enumerate iterator 61 | func (e *Enumerate) M__iter__() (Object, error) { 62 | return &EnumerateIterator{ 63 | Enumerate: *e, 64 | Index: e.Start, 65 | }, nil 66 | } 67 | 68 | // EnumerateIterator iterator 69 | func (ei *EnumerateIterator) M__iter__() (Object, error) { 70 | return ei, nil 71 | } 72 | 73 | // EnumerateIterator iterator next 74 | func (ei *EnumerateIterator) M__next__() (Object, error) { 75 | value, err := Next(ei.Enumerate.Iterable) 76 | if err != nil { 77 | return nil, err 78 | } 79 | res := make(Tuple, 2) 80 | res[0] = ei.Index 81 | res[1] = value 82 | ei.Index += 1 83 | return res, nil 84 | } 85 | 86 | // Check interface is satisfied 87 | var _ I__iter__ = (*Enumerate)(nil) 88 | var _ I_iterator = (*EnumerateIterator)(nil) 89 | -------------------------------------------------------------------------------- /py/tests/set.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | from libtest import assertRaises 6 | 7 | doc="__and__" 8 | a = {1, 2, 3} 9 | b = {2, 3, 4, 5} 10 | c = a.__and__(b) 11 | assert 2 in c 12 | assert 3 in c 13 | 14 | d = a & b 15 | assert 2 in d 16 | assert 3 in d 17 | 18 | doc="__or__" 19 | a = {1, 2, 3} 20 | b = {2, 3, 4, 5} 21 | c = a.__or__(b) 22 | assert 1 in c 23 | assert 2 in c 24 | assert 3 in c 25 | assert 4 in c 26 | assert 5 in c 27 | 28 | d = a | b 29 | assert 1 in c 30 | assert 2 in c 31 | assert 3 in c 32 | assert 4 in c 33 | assert 5 in c 34 | 35 | doc="__sub__" 36 | a = {1, 2, 3} 37 | b = {2, 3, 4, 5} 38 | c = a.__sub__(b) 39 | d = b.__sub__(a) 40 | assert 1 in c 41 | assert 4 in d 42 | assert 5 in d 43 | 44 | e = a - b 45 | f = b - a 46 | assert 1 in c 47 | assert 4 in d 48 | assert 5 in d 49 | 50 | doc="__xor__" 51 | a = {1, 2, 3} 52 | b = {2, 3, 4, 5} 53 | c = a.__xor__(b) 54 | assert 1 in c 55 | assert 4 in c 56 | assert 5 in c 57 | 58 | d = a ^ b 59 | assert 1 in c 60 | 61 | doc="__repr__" 62 | a = {1, 2, 3} 63 | b = a.__repr__() 64 | assert "{" in b 65 | assert "1" in b 66 | assert "2" in b 67 | assert "3" in b 68 | assert "}" in b 69 | 70 | doc="set" 71 | a = set([1,2,3]) 72 | b = set("set") 73 | c = set((4,5)) 74 | assert len(a) == 3 75 | assert len(b) == 3 76 | assert len(c) == 2 77 | assert 1 in a 78 | assert 2 in a 79 | assert 3 in a 80 | assert "s" in b 81 | assert "e" in b 82 | assert "t" in b 83 | assert 4 in c 84 | assert 5 in c 85 | 86 | doc="add" 87 | a = set() 88 | a.add(1) 89 | a.add(2) 90 | a.add(3) 91 | assert len(a) == 3 92 | assert 1 in a 93 | assert 2 in a 94 | assert 3 in a 95 | assert 4 not in a 96 | assertRaises(TypeError, lambda: a.add()) 97 | 98 | doc="__eq__, __ne__" 99 | a = set([1,2,3]) 100 | assert a.__eq__(3) != True 101 | assert a.__ne__(3) != False 102 | assert a.__ne__(3) != True 103 | assert a.__ne__(3) != False # This part should be changed in comparison with NotImplemented 104 | 105 | assert a.__ne__(set()) == True 106 | assert a.__eq__({1,2,3}) == True 107 | assert a.__ne__({1,2,3}) == False 108 | 109 | doc="finished" 110 | -------------------------------------------------------------------------------- /py/classmethod.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // ClassMethod objects 6 | 7 | package py 8 | 9 | var ClassMethodType = ObjectType.NewType("classmethod", 10 | `classmethod(function) -> method 11 | 12 | Convert a function to be a class method. 13 | 14 | A class method receives the class as implicit first argument, 15 | just like an instance method receives the instance. 16 | To declare a class method, use this idiom: 17 | 18 | class C: 19 | def f(cls, arg1, arg2, ...): ... 20 | f = classmethod(f) 21 | 22 | It can be called either on the class (e.g. C.f()) or on an instance 23 | (e.g. C().f()). The instance is ignored except for its class. 24 | If a class method is called for a derived class, the derived class 25 | object is passed as the implied first argument. 26 | 27 | Class methods are different than C++ or Java static methods. 28 | If you want those, see the staticmethod builtin.`, ClassMethodNew, nil) 29 | 30 | type ClassMethod struct { 31 | Callable Object 32 | Dict StringDict 33 | } 34 | 35 | // Type of this ClassMethod object 36 | func (o ClassMethod) Type() *Type { 37 | return ClassMethodType 38 | } 39 | 40 | // Get the Dict 41 | func (c *ClassMethod) GetDict() StringDict { 42 | return c.Dict 43 | } 44 | 45 | // ClassMethodNew 46 | func ClassMethodNew(metatype *Type, args Tuple, kwargs StringDict) (res Object, err error) { 47 | c := &ClassMethod{ 48 | Dict: make(StringDict), 49 | } 50 | err = UnpackTuple(args, kwargs, "classmethod", 1, 1, &c.Callable) 51 | if err != nil { 52 | return nil, err 53 | } 54 | return c, nil 55 | } 56 | 57 | // Read a classmethod from a class which makes a bound method 58 | func (c *ClassMethod) M__get__(instance, owner Object) (Object, error) { 59 | if owner == nil { 60 | owner = instance.Type() 61 | } 62 | return NewBoundMethod(owner, c.Callable), nil 63 | } 64 | 65 | // Properties 66 | func init() { 67 | ClassMethodType.Dict["__func__"] = &Property{ 68 | Fget: func(self Object) (Object, error) { 69 | return self.(*ClassMethod).Callable, nil 70 | }, 71 | } 72 | } 73 | 74 | // Check interface is satisfied 75 | var _ IGetDict = (*ClassMethod)(nil) 76 | var _ I__get__ = (*ClassMethod)(nil) 77 | -------------------------------------------------------------------------------- /vm/vm_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package vm_test 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | "strings" 11 | "sync" 12 | "testing" 13 | 14 | "github.com/go-python/gpython/py" 15 | "github.com/go-python/gpython/pytest" 16 | ) 17 | 18 | func TestVm(t *testing.T) { 19 | pytest.RunTests(t, "tests") 20 | } 21 | 22 | func BenchmarkVM(b *testing.B) { 23 | pytest.RunBenchmarks(b, "benchmarks") 24 | } 25 | 26 | var jobSrcTemplate = ` 27 | 28 | doc="multi py.Context text" 29 | WORKER_ID = "{{WORKER_ID}}" 30 | def fib(n): 31 | if n == 0: 32 | return 0 33 | elif n == 1: 34 | return 1 35 | return fib(n - 2) + fib(n - 1) 36 | 37 | x = {{FIB_TO}} 38 | fx = fib(x) 39 | print("%s says fib(%d) is %d" % (WORKER_ID, x, fx)) 40 | ` 41 | 42 | type worker struct { 43 | name string 44 | ctx py.Context 45 | } 46 | 47 | // run loads a specific worker with a specific work load 48 | func (w *worker) run(b testing.TB, pySrc string, countUpto int) { 49 | pySrc = strings.Replace(pySrc, "{{WORKER_ID}}", w.name, -1) 50 | pySrc = strings.Replace(pySrc, "{{FIB_TO}}", strconv.Itoa(countUpto), -1) 51 | 52 | module, code := pytest.CompileSrc(b, w.ctx, pySrc, w.name) 53 | _, err := w.ctx.RunCode(code, module.Globals, module.Globals, nil) 54 | if err != nil { 55 | b.Fatal(err) 56 | } 57 | } 58 | 59 | func BenchmarkContext(b *testing.B) { 60 | numWorkers := 4 61 | workersRunning := sync.WaitGroup{} 62 | 63 | numJobs := 35 64 | fmt.Printf("Starting %d workers to process %d jobs...\n", numWorkers, numJobs) 65 | 66 | jobPipe := make(chan int) 67 | go func() { 68 | for i := 0; i < numJobs; i++ { 69 | jobPipe <- i + 1 70 | } 71 | close(jobPipe) 72 | }() 73 | 74 | workers := make([]worker, numWorkers) 75 | for i := 0; i < numWorkers; i++ { 76 | 77 | workers[i] = worker{ 78 | name: fmt.Sprintf("Worker #%d", i+1), 79 | ctx: py.NewContext(py.DefaultContextOpts()), 80 | } 81 | 82 | workersRunning.Add(1) 83 | w := workers[i] 84 | go func() { 85 | for jobID := range jobPipe { 86 | w.run(b, jobSrcTemplate, jobID) 87 | //fmt.Printf("### %s finished job %v ###\n", w.name, jobID) 88 | } 89 | workersRunning.Done() 90 | }() 91 | } 92 | 93 | workersRunning.Wait() 94 | } 95 | -------------------------------------------------------------------------------- /compile/legacy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package compile 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "os" 11 | "os/exec" 12 | "strings" 13 | 14 | "github.com/go-python/gpython/py" 15 | "github.com/go-python/gpython/stdlib/marshal" 16 | ) 17 | 18 | // Compile with python3.4 - not used any more but keep for the moment! 19 | 20 | // Compile(source, filename, mode, flags, dont_inherit) -> code object 21 | // 22 | // Compile the source string (a Python module, statement or expression) 23 | // into a code object that can be executed by exec() or eval(). 24 | // The filename will be used for run-time error messages. 25 | // The mode must be 'exec' to compile a module, 'single' to compile a 26 | // single (interactive) statement, or 'eval' to compile an expression. 27 | // The flags argument, if present, controls which future statements influence 28 | // the compilation of the code. 29 | // The dont_inherit argument, if non-zero, stops the compilation inheriting 30 | // the effects of any future statements in effect in the code calling 31 | // compile; if absent or zero these statements do influence the compilation, 32 | // in addition to any features explicitly specified. 33 | func LegacyCompile(str, filename, mode string, flags int, dont_inherit bool) py.Object { 34 | dont_inherit_str := "False" 35 | if dont_inherit { 36 | dont_inherit_str = "True" 37 | } 38 | // FIXME escaping in filename 39 | code := fmt.Sprintf(`import sys, marshal 40 | str = sys.stdin.buffer.read().decode("utf-8") 41 | code = compile(str, "%s", "%s", %d, %s) 42 | marshalled_code = marshal.dumps(code) 43 | sys.stdout.buffer.write(marshalled_code) 44 | sys.stdout.close()`, 45 | filename, 46 | mode, 47 | flags, 48 | dont_inherit_str, 49 | ) 50 | cmd := exec.Command("python3.4", "-c", code) 51 | cmd.Stdin = strings.NewReader(str) 52 | var out bytes.Buffer 53 | cmd.Stdout = &out 54 | var stderr bytes.Buffer 55 | cmd.Stderr = &stderr 56 | err := cmd.Run() 57 | if err != nil { 58 | fmt.Fprintf(os.Stderr, "--- Failed to run python3.4 compile ---\n") 59 | fmt.Fprintf(os.Stderr, "--------------------\n") 60 | _, _ = os.Stderr.Write(stderr.Bytes()) 61 | fmt.Fprintf(os.Stderr, "--------------------\n") 62 | panic(err) 63 | } 64 | obj, err := marshal.ReadObject(bytes.NewBuffer(out.Bytes())) 65 | if err != nil { 66 | panic(err) 67 | } 68 | return obj 69 | } 70 | -------------------------------------------------------------------------------- /py/tests/dict.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | from libtest import assertRaises 5 | 6 | doc="str" 7 | assert str({}) == "{}" 8 | a = str({"a":"b","c":5.5}) 9 | assert a == "{'a': 'b', 'c': 5.5}" or a == "{'c': 5.5, 'a': 'b'}" 10 | 11 | doc="repr" 12 | assert repr({}) == "{}" 13 | a = repr({"a":"b","c":5.5}) 14 | assert a == "{'a': 'b', 'c': 5.5}" or a == "{'c': 5.5, 'a': 'b'}" 15 | 16 | doc="check __iter__" 17 | a = {"a":"b","c":5.5} 18 | l = list(iter(a)) 19 | assert "a" in l 20 | assert "c" in l 21 | assert len(l) == 2 22 | 23 | doc="check get" 24 | a = {"a":1} 25 | assert a.get('a') == 1 26 | assert a.get('a',100) == 1 27 | assert a.get('b') == None 28 | assert a.get('b',1) == 1 29 | assert a.get('b',True) == True 30 | 31 | doc="check keys" 32 | a = {"a":1} 33 | assert list(a.keys()) == ["a"] 34 | 35 | doc="check values" 36 | a = {"a":1} 37 | assert list(a.values()) == [1] 38 | 39 | doc="check items" 40 | a = {"a":"b","c":5.5} 41 | for k, v in a.items(): 42 | assert k in ["a", "c"] 43 | if k == "a": 44 | assert v == "b" 45 | if k == "c": 46 | assert v == 5.5 47 | assertRaises(TypeError, a.items, 'a') 48 | 49 | doc="del" 50 | a = {'hello': 'world', 'hi': 'there'} 51 | del a["hello"] 52 | def doDel(d, key): 53 | del d[key] 54 | assertRaises(KeyError, lambda: doDel(a, "bob")) 55 | assertRaises(KeyError, lambda: doDel(a, 123)) 56 | assert not a.__contains__('hello') 57 | assert a.__contains__('hi') 58 | 59 | doc="init" 60 | a = dict( zip( "a,b,c".split(","), "1,2,3".split(",") ) ) 61 | assert a["a"] == "1" 62 | assert a["b"] == "2" 63 | assert a["c"] == "3" 64 | 65 | a = dict(a="1", b="2", c="3") 66 | assert a["a"] == "1" 67 | assert a["b"] == "2" 68 | assert a["c"] == "3" 69 | 70 | assertRaises(TypeError, dict, "a") 71 | assertRaises(TypeError, dict, 1) 72 | assertRaises(TypeError, dict, {"a":1}, {"b":2}) 73 | 74 | doc="__contain__" 75 | a = {'hello': 'world'} 76 | assert a.__contains__('hello') 77 | assert not a.__contains__('world') 78 | 79 | doc="__eq__, __ne__" 80 | a = {'a': 'b'} 81 | assert a.__eq__(3) != True 82 | assert a.__ne__(3) != False 83 | assert a.__ne__(3) != True 84 | assert a.__ne__(3) != False 85 | 86 | assert a.__ne__({}) == True 87 | assert a.__eq__({'a': 'b'}) == True 88 | assert a.__ne__({'a': 'b'}) == False 89 | 90 | doc="__len__" 91 | a = {"a": "1", "b": "2"} 92 | assert a.__len__() == 2 93 | assert len(a) == 2 94 | 95 | doc="finished" 96 | -------------------------------------------------------------------------------- /vm/builtin.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Implement builtin functions eval and exec 6 | 7 | package vm 8 | 9 | import ( 10 | "strings" 11 | 12 | "github.com/go-python/gpython/py" 13 | ) 14 | 15 | func builtinEvalOrExec(ctx py.Context, args py.Tuple, kwargs, currentLocals, currentGlobals, builtins py.StringDict, mode py.CompileMode) (py.Object, error) { 16 | var ( 17 | cmd py.Object 18 | globals py.Object = py.None 19 | locals py.Object = py.None 20 | ) 21 | err := py.UnpackTuple(args, kwargs, string(mode), 1, 3, &cmd, &globals, &locals) 22 | if err != nil { 23 | return nil, err 24 | } 25 | if globals == py.None { 26 | globals = currentGlobals 27 | if locals == py.None { 28 | locals = currentLocals 29 | } 30 | } else if locals == py.None { 31 | locals = globals 32 | } 33 | // FIXME this can be a mapping too 34 | globalsDict, err := py.DictCheck(globals) 35 | if err != nil { 36 | return nil, py.ExceptionNewf(py.TypeError, "globals must be a dict") 37 | } 38 | localsDict, err := py.DictCheck(locals) 39 | if err != nil { 40 | return nil, py.ExceptionNewf(py.TypeError, "locals must be a dict") 41 | } 42 | 43 | // Set __builtins__ if not set 44 | if _, ok := globalsDict["__builtins__"]; !ok { 45 | globalsDict["__builtins__"] = builtins 46 | } 47 | 48 | var codeStr string 49 | var code *py.Code 50 | switch x := cmd.(type) { 51 | case *py.Code: 52 | code = x 53 | case py.String: 54 | codeStr = string(x) 55 | case py.Bytes: 56 | codeStr = string(x) 57 | default: 58 | return nil, py.ExceptionNewf(py.TypeError, "%s() arg 1 must be a string, bytes or code object", mode) 59 | 60 | } 61 | if code == nil { 62 | codeStr = strings.TrimLeft(codeStr, " \t") 63 | code, err = py.Compile(codeStr, "", mode, 0, true) 64 | if err != nil { 65 | return nil, err 66 | } 67 | } 68 | if code.GetNumFree() > 0 { 69 | return nil, py.ExceptionNewf(py.TypeError, "code passed to %s() may not contain free variables", mode) 70 | } 71 | return ctx.RunCode(code, globalsDict, localsDict, nil) 72 | } 73 | 74 | func builtinEval(ctx py.Context, args py.Tuple, kwargs, currentLocals, currentGlobals, builtins py.StringDict) (py.Object, error) { 75 | return builtinEvalOrExec(ctx, args, kwargs, currentLocals, currentGlobals, builtins, py.EvalMode) 76 | } 77 | 78 | func builtinExec(ctx py.Context, args py.Tuple, kwargs, currentLocals, currentGlobals, builtins py.StringDict) (py.Object, error) { 79 | _, err := builtinEvalOrExec(ctx, args, kwargs, currentLocals, currentGlobals, builtins, py.ExecMode) 80 | if err != nil { 81 | return nil, err 82 | } 83 | return py.None, nil 84 | } 85 | -------------------------------------------------------------------------------- /repl/web/main.go: -------------------------------------------------------------------------------- 1 | // An online REPL for gpython using wasm 2 | 3 | // Copyright 2018 The go-python Authors. All rights reserved. 4 | // Use of this source code is governed by a BSD-style 5 | // license that can be found in the LICENSE file. 6 | 7 | //go:build js 8 | // +build js 9 | 10 | package main 11 | 12 | import ( 13 | "log" 14 | "runtime" 15 | 16 | "github.com/go-python/gpython/repl" 17 | "github.com/gopherjs/gopherwasm/js" // gopherjs to wasm converter shim 18 | 19 | // import required modules 20 | _ "github.com/go-python/gpython/stdlib" 21 | ) 22 | 23 | // Implement the replUI interface 24 | type termIO struct { 25 | js.Value 26 | } 27 | 28 | // SetPrompt sets the UI prompt 29 | func (t *termIO) SetPrompt(prompt string) { 30 | t.Call("set_prompt", prompt) 31 | } 32 | 33 | // Print outputs the string to the output 34 | func (t *termIO) Print(out string) { 35 | t.Call("echo", out) 36 | } 37 | 38 | var document js.Value 39 | 40 | func isUndefined(node js.Value) bool { 41 | return node == js.Undefined() 42 | } 43 | 44 | func getElementById(name string) js.Value { 45 | node := document.Call("getElementById", name) 46 | if isUndefined(node) { 47 | log.Fatalf("Couldn't find element %q", name) 48 | } 49 | return node 50 | } 51 | 52 | func running() string { 53 | switch { 54 | case runtime.GOOS == "js" && runtime.GOARCH == "wasm": 55 | return "go/wasm" 56 | case runtime.GOARCH == "js": 57 | return "gopherjs" 58 | } 59 | return "unknown" 60 | } 61 | 62 | func main() { 63 | document = js.Global().Get("document") 64 | if isUndefined(document) { 65 | log.Fatalf("Didn't find document - not running in browser") 66 | } 67 | 68 | // Clear the loading text 69 | termNode := getElementById("term") 70 | termNode.Set("innerHTML", "") 71 | 72 | // work out what we are running on and mark active 73 | tech := running() 74 | node := getElementById(tech) 75 | node.Get("classList").Call("add", "active") 76 | 77 | // Make a repl referring to an empty term for the moment 78 | REPL := repl.New(nil) 79 | cb := js.NewCallback(func(args []js.Value) { 80 | REPL.Run(args[0].String()) 81 | }) 82 | 83 | // Create a jquery terminal instance 84 | opts := js.ValueOf(map[string]interface{}{ 85 | "greetings": "Gpython 3.4.0 running in your browser with " + tech, 86 | "name": "gpython", 87 | "prompt": repl.NormalPrompt, 88 | }) 89 | terminal := js.Global().Call("$", "#term").Call("terminal", cb, opts) 90 | 91 | // Send the console log direct to the terminal 92 | js.Global().Get("console").Set("log", terminal.Get("echo")) 93 | 94 | // Set the implementation of term 95 | REPL.SetUI(&termIO{terminal}) 96 | 97 | // wait for callbacks 98 | select {} 99 | } 100 | -------------------------------------------------------------------------------- /py/bool.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Bool objects 6 | 7 | package py 8 | 9 | type Bool bool 10 | 11 | var ( 12 | BoolType = NewType("bool", "bool(x) -> bool\n\nReturns True when the argument x is true, False otherwise.\nThe builtins True and False are the only two instances of the class bool.\nThe class bool is a subclass of the class int, and cannot be subclassed.") 13 | // Some well known bools 14 | False = Bool(false) 15 | True = Bool(true) 16 | ) 17 | 18 | // Type of this object 19 | func (s Bool) Type() *Type { 20 | return BoolType 21 | } 22 | 23 | // Make a new bool - returns the canonical True and False values 24 | func NewBool(t bool) Bool { 25 | if t { 26 | return True 27 | } 28 | return False 29 | } 30 | 31 | func (a Bool) M__bool__() (Object, error) { 32 | return a, nil 33 | } 34 | 35 | func (a Bool) M__index__() (Int, error) { 36 | if a { 37 | return Int(1), nil 38 | } 39 | return Int(0), nil 40 | } 41 | 42 | func (a Bool) M__str__() (Object, error) { 43 | return a.M__repr__() 44 | } 45 | 46 | func (a Bool) M__repr__() (Object, error) { 47 | if a { 48 | return String("True"), nil 49 | } 50 | return String("False"), nil 51 | } 52 | 53 | // Convert an Object to an Bool 54 | // 55 | // Returns ok as to whether the conversion worked or not 56 | func convertToBool(other Object) (Bool, bool) { 57 | switch b := other.(type) { 58 | case Bool: 59 | return b, true 60 | case Int: 61 | switch b { 62 | case 0: 63 | return False, true 64 | case 1: 65 | return True, true 66 | default: 67 | return False, false 68 | } 69 | case Float: 70 | switch b { 71 | case 0: 72 | return False, true 73 | case 1: 74 | return True, true 75 | default: 76 | return False, false 77 | } 78 | } 79 | return False, false 80 | } 81 | 82 | func (a Bool) M__eq__(other Object) (Object, error) { 83 | if b, ok := convertToBool(other); ok { 84 | return NewBool(a == b), nil 85 | } 86 | return False, nil 87 | } 88 | 89 | func (a Bool) M__ne__(other Object) (Object, error) { 90 | if b, ok := convertToBool(other); ok { 91 | return NewBool(a != b), nil 92 | } 93 | return True, nil 94 | } 95 | 96 | func notEq(eq Object, err error) (Object, error) { 97 | if err != nil { 98 | return nil, err 99 | } 100 | if eq == NotImplemented { 101 | return eq, nil 102 | } 103 | return Not(eq) 104 | } 105 | 106 | // Check interface is satisfied 107 | var _ I__bool__ = Bool(false) 108 | var _ I__index__ = Bool(false) 109 | var _ I__str__ = Bool(false) 110 | var _ I__repr__ = Bool(false) 111 | var _ I__eq__ = Bool(false) 112 | var _ I__ne__ = Bool(false) 113 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Gpython binary 6 | 7 | package main 8 | 9 | import ( 10 | "flag" 11 | "fmt" 12 | "log" 13 | "os" 14 | "runtime" 15 | "runtime/pprof" 16 | 17 | "github.com/go-python/gpython/py" 18 | "github.com/go-python/gpython/repl" 19 | "github.com/go-python/gpython/repl/cli" 20 | 21 | _ "github.com/go-python/gpython/stdlib" 22 | ) 23 | 24 | var ( 25 | cpuprofile = flag.String("cpuprofile", "", "Write cpu profile to file") 26 | ) 27 | 28 | // syntaxError prints the syntax 29 | func syntaxError() { 30 | fmt.Fprintf(os.Stderr, `GPython 31 | 32 | A python implementation in Go 33 | 34 | Full options: 35 | `) 36 | flag.PrintDefaults() 37 | } 38 | 39 | func main() { 40 | flag.Usage = syntaxError 41 | flag.Parse() 42 | xmain(flag.Args()) 43 | } 44 | 45 | func xmain(args []string) { 46 | opts := py.DefaultContextOpts() 47 | opts.SysArgs = args 48 | ctx := py.NewContext(opts) 49 | defer ctx.Close() 50 | 51 | if *cpuprofile != "" { 52 | f, err := os.Create(*cpuprofile) 53 | if err != nil { 54 | log.Fatal(err) 55 | } 56 | err = pprof.StartCPUProfile(f) 57 | if err != nil { 58 | log.Fatal(err) 59 | } 60 | defer pprof.StopCPUProfile() 61 | } 62 | 63 | var err error 64 | // IF no args, enter REPL mode 65 | if len(args) == 0 { 66 | 67 | fmt.Printf("Python 3.4.0 (%s, %s)\n", commit, date) 68 | fmt.Printf("[Gpython %s]\n", version) 69 | fmt.Printf("- os/arch: %s/%s\n", runtime.GOOS, runtime.GOARCH) 70 | fmt.Printf("- go version: %s\n", runtime.Version()) 71 | 72 | replCtx := repl.New(ctx) 73 | err = cli.RunREPL(replCtx) 74 | } else { 75 | _, err = py.RunFile(ctx, args[0], py.CompileOpts{}, nil) 76 | } 77 | if err != nil { 78 | if py.IsException(py.SystemExit, err) { 79 | handleSystemExit(err.(py.ExceptionInfo).Value.(*py.Exception)) 80 | } 81 | py.TracebackDump(err) 82 | os.Exit(1) 83 | } 84 | } 85 | 86 | func handleSystemExit(exc *py.Exception) { 87 | args := exc.Args.(py.Tuple) 88 | if len(args) == 0 { 89 | os.Exit(0) 90 | } else if len(args) == 1 { 91 | if code, ok := args[0].(py.Int); ok { 92 | c, err := code.GoInt() 93 | if err != nil { 94 | fmt.Fprintln(os.Stderr, err) 95 | os.Exit(1) 96 | } 97 | os.Exit(c) 98 | } 99 | msg, err := py.ReprAsString(args[0]) 100 | if err != nil { 101 | fmt.Fprintln(os.Stderr, err) 102 | } else { 103 | fmt.Fprintln(os.Stderr, msg) 104 | } 105 | os.Exit(1) 106 | } else { 107 | msg, err := py.ReprAsString(args) 108 | if err != nil { 109 | fmt.Fprintln(os.Stderr, err) 110 | } else { 111 | fmt.Fprintln(os.Stderr, msg) 112 | } 113 | os.Exit(1) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /ci/run-tests.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import ( 11 | "bufio" 12 | "bytes" 13 | "flag" 14 | "fmt" 15 | "log" 16 | "os" 17 | "os/exec" 18 | "strings" 19 | "time" 20 | ) 21 | 22 | func main() { 23 | log.SetPrefix("ci: ") 24 | log.SetFlags(0) 25 | 26 | start := time.Now() 27 | defer func() { 28 | log.Printf("elapsed time: %v\n", time.Since(start)) 29 | }() 30 | 31 | var ( 32 | race = flag.Bool("race", false, "enable race detector") 33 | cover = flag.String("coverpkg", "", "apply coverage analysis in each test to packages matching the patterns.") 34 | tags = flag.String("tags", "", "build tags") 35 | verbose = flag.Bool("v", false, "enable verbose output") 36 | ) 37 | 38 | flag.Parse() 39 | 40 | pkgs, err := pkgList() 41 | if err != nil { 42 | log.Fatal(err) 43 | } 44 | 45 | f, err := os.Create("coverage.txt") 46 | if err != nil { 47 | log.Fatal(err) 48 | } 49 | defer f.Close() 50 | 51 | args := []string{"test"} 52 | 53 | if *verbose { 54 | args = append(args, "-v") 55 | } 56 | if *cover != "" { 57 | args = append(args, "-coverprofile=profile.out", "-covermode=atomic", "-coverpkg="+*cover) 58 | } 59 | if *tags != "" { 60 | args = append(args, "-tags="+*tags) 61 | } 62 | switch { 63 | case *race: 64 | args = append(args, "-race", "-timeout=20m") 65 | default: 66 | args = append(args, "-timeout=10m") 67 | } 68 | args = append(args, "") 69 | 70 | for _, pkg := range pkgs { 71 | args[len(args)-1] = pkg 72 | cmd := exec.Command("go", args...) 73 | cmd.Stdin = os.Stdin 74 | cmd.Stdout = os.Stdout 75 | cmd.Stderr = os.Stderr 76 | err := cmd.Run() 77 | if err != nil { 78 | log.Fatal(err) 79 | } 80 | if *cover != "" { 81 | profile, err := os.ReadFile("profile.out") 82 | if err != nil { 83 | log.Fatal(err) 84 | } 85 | _, err = f.Write(profile) 86 | if err != nil { 87 | log.Fatal(err) 88 | } 89 | os.Remove("profile.out") 90 | } 91 | } 92 | 93 | err = f.Close() 94 | if err != nil { 95 | log.Fatal(err) 96 | } 97 | } 98 | 99 | func pkgList() ([]string, error) { 100 | out := new(bytes.Buffer) 101 | cmd := exec.Command("go", "list", "./...") 102 | cmd.Stdout = out 103 | cmd.Stderr = os.Stderr 104 | cmd.Stdin = os.Stdin 105 | 106 | err := cmd.Run() 107 | if err != nil { 108 | return nil, fmt.Errorf("could not get package list: %w", err) 109 | } 110 | 111 | var pkgs []string 112 | scan := bufio.NewScanner(out) 113 | for scan.Scan() { 114 | pkg := scan.Text() 115 | if strings.Contains(pkg, "vendor") { 116 | continue 117 | } 118 | pkgs = append(pkgs, pkg) 119 | } 120 | 121 | return pkgs, nil 122 | } 123 | -------------------------------------------------------------------------------- /py/traceback.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Traceback objects 6 | 7 | package py 8 | 9 | import ( 10 | "fmt" 11 | "io" 12 | "os" 13 | ) 14 | 15 | // A python Traceback object 16 | type Traceback struct { 17 | Next *Traceback 18 | Frame *Frame 19 | Lasti int32 20 | Lineno int32 21 | } 22 | 23 | var TracebackType = NewType("traceback", "A python traceback") 24 | 25 | // Type of this object 26 | func (o *Traceback) Type() *Type { 27 | return TracebackType 28 | } 29 | 30 | // Make a new traceback 31 | func NewTraceback(next *Traceback, frame *Frame, lasti, lineno int32) *Traceback { 32 | return &Traceback{ 33 | Next: next, 34 | Frame: frame, 35 | Lasti: lasti, 36 | Lineno: lineno, 37 | } 38 | } 39 | 40 | /* 41 | Traceback (most recent call last): 42 | File "throws.py", line 8, in 43 | main() 44 | File "throws.py", line 5, in main 45 | throws() 46 | File "throws.py", line 2, in throws 47 | raise RuntimeError('this is the error message') 48 | RuntimeError: this is the error message 49 | */ 50 | 51 | // Dump a traceback for tb to w 52 | func (tb *Traceback) TracebackDump(w io.Writer) { 53 | for ; tb != nil; tb = tb.Next { 54 | fmt.Fprintf(w, " File %q, line %d, in %s\n", tb.Frame.Code.Filename, tb.Lineno, tb.Frame.Code.Name) 55 | //fmt.Fprintf(w, " %s\n", "FIXME line of source goes here") 56 | } 57 | } 58 | 59 | // Dumps a traceback to stderr 60 | func TracebackDump(err interface{}) { 61 | switch e := err.(type) { 62 | case ExceptionInfo: 63 | e.TracebackDump(os.Stderr) 64 | case *ExceptionInfo: 65 | e.TracebackDump(os.Stderr) 66 | case *Exception: 67 | fmt.Fprintf(os.Stderr, "Exception %v\n", e) 68 | fmt.Fprintf(os.Stderr, "-- No traceback available --\n") 69 | default: 70 | fmt.Fprintf(os.Stderr, "Error %v\n", err) 71 | fmt.Fprintf(os.Stderr, "-- No traceback available --\n") 72 | } 73 | } 74 | 75 | // Properties 76 | func init() { 77 | TracebackType.Dict["__tb_next__"] = &Property{ 78 | Fget: func(self Object) (Object, error) { 79 | next := self.(*Traceback).Next 80 | if next == nil { 81 | return None, nil 82 | } 83 | return next, nil 84 | }, 85 | } 86 | TracebackType.Dict["__tb_frame__"] = &Property{ 87 | Fget: func(self Object) (Object, error) { 88 | return self.(*Traceback).Frame, nil 89 | }, 90 | } 91 | TracebackType.Dict["__tb_lasti__"] = &Property{ 92 | Fget: func(self Object) (Object, error) { 93 | return Int(self.(*Traceback).Lasti), nil 94 | }, 95 | } 96 | TracebackType.Dict["__tb_lineno__"] = &Property{ 97 | Fget: func(self Object) (Object, error) { 98 | return Int(self.(*Traceback).Lineno), nil 99 | }, 100 | } 101 | } 102 | 103 | // Make sure it satisfies the interface 104 | var _ Object = (*Traceback)(nil) 105 | -------------------------------------------------------------------------------- /vm/tests/ops.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | _2 = 2 6 | _10 = 10 7 | _11 = 11 8 | _100 = 100 9 | a=[0,1,2,3,4] 10 | 11 | doc="Unary Ops" 12 | assert +_2 == 2 13 | assert -_2 == -2 14 | assert not _2 is False 15 | assert ~_2 == -3 16 | 17 | doc="Binary Ops" 18 | assert _2**_10 == 1024 19 | assert _2*_10 == 20 20 | assert _10//_2 == 5 21 | assert _11//_2 == 5 22 | assert _10/_2 == 5.0 23 | assert _11/_2 == 5.5 24 | assert _10 % _2 == 0 25 | assert _11 % _2 == 1 26 | assert _10 + _2 == 12 27 | assert _10 - _2 == 8 28 | assert a[1] == 1 29 | assert a[4] == 4 30 | assert _2 << _10 == 2048 31 | assert _100 >> 2 == 25 32 | assert _10 & _2 == 2 33 | assert _100 | _2 == 102 34 | assert _10 ^ _2 == 8 35 | 36 | doc="Inplace Ops" 37 | a = _2 38 | a **= _10 39 | assert a == 1024 40 | a = _2 41 | a *= _10 42 | assert a == 20 43 | a = _10 44 | a //= _2 45 | assert a == 5 46 | a = _11 47 | a //= _2 48 | assert a == 5 49 | a = _10 50 | a /= _2 51 | assert a == 5.0 52 | a = _11 53 | a /= _2 54 | assert a == 5.5 55 | a = _10 56 | a %= _2 57 | assert a == 0 58 | a = _11 59 | a %= _2 60 | assert a == 1 61 | a = _10 62 | a += _2 63 | assert a == 12 64 | a = _10 65 | a -= _2 66 | assert a == 8 67 | a = _2 68 | a <<= _10 69 | assert a == 2048 70 | a = _100 71 | a >>= 2 72 | assert a == 25 73 | a = _10 74 | a &= _2 75 | assert a == 2 76 | a = _100 77 | a |= _2 78 | assert a == 102 79 | a = _10 80 | a ^= _2 81 | assert a == 8 82 | 83 | doc="Comparison" 84 | assert _2 < _10 85 | assert _2 <= _10 86 | assert _2 <= _2 87 | assert _2 == _2 88 | assert _2 != _10 89 | assert _10 > _2 90 | assert _10 >= _2 91 | assert _2 >= _2 92 | assert _2 in (1,2,3) 93 | assert _100 not in (1,2,3) 94 | assert True is True 95 | assert True is not False 96 | # FIXME EXC_MATCH 97 | 98 | doc="Multiple comparison" 99 | 100 | assert _2 < _10 < _11 < _100 101 | assert not (_10 < _2 < _11 < _100) 102 | assert _100 > _11 > _10 > _2 103 | 104 | doc="logical" 105 | t = True 106 | f = False 107 | assert (f and f) == False 108 | assert (f and t) == False 109 | assert (t and f) == False 110 | assert (t and t) == True 111 | 112 | assert (f and f and f) == False 113 | assert (f and f and t) == False 114 | assert (f and t and f) == False 115 | assert (f and t and t) == False 116 | assert (t and f and f) == False 117 | assert (t and f and t) == False 118 | assert (t and t and f) == False 119 | assert (t and t and t) == True 120 | 121 | assert (f or f) == False 122 | assert (f or t) == True 123 | assert (t or f) == True 124 | assert (t or t) == True 125 | 126 | assert (f or f or f) == False 127 | assert (f or f or t) == True 128 | assert (f or t or f) == True 129 | assert (f or t or t) == True 130 | assert (t or f or f) == True 131 | assert (t or f or t) == True 132 | assert (t or t or f) == True 133 | assert (t or t or t) == True 134 | 135 | doc="finished" 136 | -------------------------------------------------------------------------------- /py3test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2018 The go-python Authors. All rights reserved. 4 | # Use of this source code is governed by a BSD-style 5 | # license that can be found in the LICENSE file. 6 | 7 | """ 8 | Check all the tests work with python3.4 and gpython 9 | 10 | Note that this isn't quite the same as running the unit tests - the 11 | unit tests should be preferred. This is a quick check to make sure 12 | the tests run with python3. 13 | """ 14 | 15 | import os 16 | import sys 17 | from subprocess import Popen, PIPE, STDOUT 18 | from collections import defaultdict 19 | 20 | py_version = "python3.4" 21 | 22 | opt_install = "/opt/"+py_version 23 | 24 | bin_dirs = os.environ["PATH"].split(os.pathsep) + [ 25 | opt_install+"/bin", 26 | os.path.join(os.environ["HOME"], "bin/"+py_version+"/bin"), 27 | ] 28 | 29 | def find_python(): 30 | """Find a version of python to run""" 31 | for bin_dir in bin_dirs: 32 | path = os.path.join(bin_dir, py_version) 33 | if os.path.exists(path): 34 | return path 35 | print("Couldn't find "+py_version+" on $PATH or "+" or ".join(bin_dirs[-2:])) 36 | print("Install "+py_version+" by doing:") 37 | print(" sudo mkdir -p "+opt_install) 38 | print(" sudo chown $USER "+opt_install) 39 | print(" ./bin/install-python.sh "+opt_install+'"') 40 | sys.exit(1) 41 | 42 | testwith = [find_python(), "gpython"] 43 | 44 | def runtests(dirpath, filenames, failures): 45 | """Run the tests found accumulating failures""" 46 | print("Running tests in %s" % dirpath) 47 | for name in filenames: 48 | if not name.endswith(".py") or name.startswith("lib") or name.startswith("raise"): 49 | continue 50 | #print(" - %s" % name) 51 | fullpath = os.path.join(dirpath, name) 52 | for cmd in testwith: 53 | prog = [cmd, fullpath] 54 | p = Popen(prog, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True) 55 | stdout, stderr = p.communicate("") 56 | rc = p.returncode 57 | if rc != 0: 58 | failures[cmd][fullpath].append(stdout.decode("utf-8")) 59 | return failures 60 | 61 | def main(): 62 | binary = os.path.abspath(__file__) 63 | home = os.path.dirname(binary) 64 | os.chdir(home) 65 | print("Scanning %s for tests" % home) 66 | 67 | failures = defaultdict(lambda: defaultdict(list)) 68 | for dirpath, dirnames, filenames in os.walk("."): 69 | if os.path.basename(dirpath) == "tests": 70 | runtests(dirpath, filenames, failures) 71 | 72 | if not failures: 73 | print("All OK") 74 | return 75 | 76 | print() 77 | 78 | sep = "="*60+"\n" 79 | sep2 = "-"*60+"\n" 80 | 81 | for cmd in sorted(failures.keys()): 82 | for path in sorted(failures[cmd].keys()): 83 | print(sep+"Failures for "+cmd+" in "+path) 84 | sys.stdout.write(sep+sep2.join(failures[cmd][path])+sep) 85 | print() 86 | sys.exit(1) 87 | 88 | 89 | if __name__ == "__main__": 90 | main() 91 | -------------------------------------------------------------------------------- /vm/tests/with.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | class Context(): 6 | def __init__(self): 7 | self.state = "__init__" 8 | def __enter__(self): 9 | self.state = "__enter__" 10 | return 42 11 | def __exit__(self, type, value, traceback): 12 | self.state = "__exit__" 13 | 14 | doc="with" 15 | c = Context() 16 | assert c.state == "__init__" 17 | with c: 18 | assert c.state == "__enter__" 19 | 20 | assert c.state == "__exit__" 21 | 22 | doc="with as" 23 | c = Context() 24 | assert c.state == "__init__" 25 | with c as a: 26 | assert a == 42 27 | assert c.state == "__enter__" 28 | 29 | assert c.state == "__exit__" 30 | 31 | doc="with exception" 32 | c = Context() 33 | ok = False 34 | try: 35 | assert c.state == "__init__" 36 | with c: 37 | assert c.state == "__enter__" 38 | raise ValueError("potato") 39 | except ValueError: 40 | ok = True 41 | assert c.state == "__exit__" 42 | assert ok, "ValueError not raised" 43 | 44 | class SilencedContext(): 45 | def __init__(self): 46 | self.state = "__init__" 47 | def __enter__(self): 48 | self.state = "__enter__" 49 | def __exit__(self, type, value, traceback): 50 | """Return True to silence the error""" 51 | self.type = type 52 | self.value = value 53 | self.traceback = traceback 54 | self.state = "__exit__" 55 | return True 56 | 57 | doc="with silenced error" 58 | c = SilencedContext() 59 | assert c.state == "__init__" 60 | with c: 61 | assert c.state == "__enter__" 62 | raise ValueError("potato") 63 | assert c.state == "__exit__" 64 | assert c.type == ValueError 65 | assert c.value is not None 66 | assert c.traceback is not None 67 | 68 | doc="with silenced error no error" 69 | c = SilencedContext() 70 | assert c.state == "__init__" 71 | with c: 72 | assert c.state == "__enter__" 73 | assert c.state == "__exit__" 74 | assert c.type is None 75 | assert c.value is None 76 | assert c.traceback is None 77 | 78 | doc="with in loop: break" 79 | c = Context() 80 | assert c.state == "__init__" 81 | while True: 82 | with c: 83 | assert c.state == "__enter__" 84 | break 85 | assert c.state == "__exit__" 86 | 87 | doc="with in loop: continue" 88 | c = Context() 89 | assert c.state == "__init__" 90 | first = True 91 | while True: 92 | if not first: 93 | break 94 | with c: 95 | assert c.state == "__enter__" 96 | first = False 97 | continue 98 | assert c.state == "__exit__" 99 | 100 | doc="return in with" 101 | c = Context() 102 | def return_in_with(): 103 | assert c.state == "__init__" 104 | first = True 105 | with c: 106 | assert c.state == "__enter__" 107 | first = False 108 | return "potato" 109 | assert return_in_with() == "potato" 110 | assert c.state == "__exit__" 111 | 112 | doc="finished" 113 | -------------------------------------------------------------------------------- /py/string_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package py 6 | 7 | import "testing" 8 | 9 | func TestStringFind(t *testing.T) { 10 | for _, tc := range []struct { 11 | str string 12 | sub string 13 | beg int 14 | end int 15 | idx int 16 | }{ 17 | { 18 | str: "hello world", 19 | sub: "world", 20 | idx: 6, 21 | }, 22 | { 23 | str: "hello world", 24 | sub: "o", 25 | idx: 4, 26 | }, 27 | { 28 | str: "hello world", 29 | sub: "o", 30 | beg: 5, 31 | idx: 7, 32 | }, 33 | { 34 | str: "hello world", 35 | sub: "bye", 36 | idx: -1, 37 | }, 38 | { 39 | str: "Hello, 世界", 40 | sub: "界", 41 | idx: 8, 42 | }, 43 | { 44 | str: "01234 6789", 45 | sub: " ", 46 | beg: 6, 47 | idx: -1, 48 | }, 49 | { 50 | str: "0123456789", 51 | sub: "6", 52 | beg: 1, 53 | end: 6, 54 | idx: -1, 55 | }, 56 | { 57 | str: "0123456789", 58 | sub: "6", 59 | beg: 1, 60 | end: 7, 61 | idx: 6, 62 | }, 63 | { 64 | str: "0123456789", 65 | sub: "6", 66 | beg: 1, 67 | end: -1, 68 | idx: 6, 69 | }, 70 | { 71 | str: "0123456789", 72 | sub: "6", 73 | beg: 100, 74 | end: -1, 75 | idx: -1, 76 | }, 77 | { 78 | str: "0123456789", 79 | sub: "6", 80 | beg: 2, 81 | end: 1, 82 | idx: -1, 83 | }, 84 | } { 85 | t.Run(tc.str+":"+tc.sub, func(t *testing.T) { 86 | beg := tc.beg 87 | end := tc.end 88 | if end == 0 { 89 | end = len(tc.str) 90 | } 91 | idx, err := String(tc.str).find(Tuple{String(tc.sub), Int(beg), Int(end)}) 92 | if err != nil { 93 | t.Fatalf("invalid: %+v", err) 94 | } 95 | if got, want := int(idx.(Int)), tc.idx; got != want { 96 | t.Fatalf("got=%d, want=%d", got, want) 97 | } 98 | }) 99 | } 100 | } 101 | 102 | func TestStringUpper(t *testing.T) { 103 | tests := []struct { 104 | name string 105 | s String 106 | want Object 107 | }{{ 108 | name: "abc", 109 | s: String("abc"), 110 | want: String("ABC")}, 111 | } 112 | for _, tt := range tests { 113 | t.Run(tt.name, func(t *testing.T) { 114 | got, err := tt.s.Upper() 115 | if err != nil { 116 | t.Fatalf("Upper() error = %v", err) 117 | } 118 | if got.(String) != tt.want.(String) { 119 | t.Fatalf("Upper() got = %v, want %v", got, tt.want) 120 | } 121 | }) 122 | } 123 | } 124 | 125 | func TestStringLower(t *testing.T) { 126 | tests := []struct { 127 | name string 128 | s String 129 | want Object 130 | }{{ 131 | name: "ABC", 132 | s: String("ABC"), 133 | want: String("abc")}, 134 | } 135 | for _, tt := range tests { 136 | t.Run(tt.name, func(t *testing.T) { 137 | got, err := tt.s.Lower() 138 | if err != nil { 139 | t.Fatalf("Lower() error = %v", err) 140 | } 141 | if got.(String) != tt.want.(String) { 142 | t.Fatalf("Lower() got = %v, want %v", got, tt.want) 143 | } 144 | }) 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /ast/dump.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ast 6 | 7 | import ( 8 | "fmt" 9 | "reflect" 10 | "strings" 11 | 12 | "github.com/go-python/gpython/py" 13 | ) 14 | 15 | func dumpItem(v interface{}) string { 16 | if v == nil { 17 | return "None" 18 | } 19 | switch x := v.(type) { 20 | case py.String: 21 | return fmt.Sprintf("'%s'", string(x)) 22 | case py.Bytes: 23 | return fmt.Sprintf("b'%s'", string(x)) 24 | case Identifier: 25 | if x == "" { 26 | return "None" 27 | } 28 | return fmt.Sprintf("'%s'", string(x)) 29 | case *Keyword: 30 | return dump(x, "keyword") 31 | case *WithItem: 32 | return dump(x, "withitem") 33 | case *Arguments: 34 | if x == nil { 35 | return "None" 36 | } 37 | return dump(x, "arguments") 38 | case *Arg: 39 | if x == nil { 40 | return "None" 41 | } 42 | return dump(x, "arg") 43 | case ModBase: 44 | case StmtBase: 45 | case ExprBase: 46 | case SliceBase: 47 | case Pos: 48 | case *Alias: 49 | return dump(v, "alias") 50 | case Ast: 51 | return Dump(x) 52 | case py.I__str__: 53 | str, err := x.M__str__() 54 | if err != nil { 55 | panic(err) 56 | } 57 | return string(str.(py.String)) 58 | case Comprehension: 59 | return dump(v, "comprehension") 60 | } 61 | return fmt.Sprintf("%v", v) 62 | } 63 | 64 | // Dump ast as a string with name 65 | func dump(ast interface{}, name string) string { 66 | astValue := reflect.Indirect(reflect.ValueOf(ast)) 67 | astType := astValue.Type() 68 | args := make([]string, 0) 69 | for i := 0; i < astType.NumField(); i++ { 70 | fieldType := astType.Field(i) 71 | fieldValue := astValue.Field(i) 72 | fname := strings.ToLower(fieldType.Name) 73 | switch fname { 74 | case "stmtbase", "exprbase", "modbase", "slicebase", "pos": 75 | continue 76 | case "exprtype": 77 | fname = "type" 78 | case "contextexpr": 79 | fname = "context_expr" 80 | case "optionalvars": 81 | fname = "optional_vars" 82 | case "kwdefaults": 83 | fname = "kw_defaults" 84 | case "decoratorlist": 85 | fname = "decorator_list" 86 | } 87 | if fieldValue.Kind() == reflect.Slice && fieldValue.Type().Elem().Kind() != reflect.Uint8 { 88 | strs := make([]string, fieldValue.Len()) 89 | for i := 0; i < fieldValue.Len(); i++ { 90 | element := fieldValue.Index(i) 91 | if element.CanInterface() { 92 | v := element.Interface() 93 | strs[i] = dumpItem(v) 94 | } 95 | } 96 | args = append(args, fmt.Sprintf("%s=[%s]", fname, strings.Join(strs, ", "))) 97 | } else if fieldValue.CanInterface() { 98 | v := fieldValue.Interface() 99 | args = append(args, fmt.Sprintf("%s=%s", fname, dumpItem(v))) 100 | } 101 | } 102 | return fmt.Sprintf("%s(%s)", name, strings.Join(args, ", ")) 103 | } 104 | 105 | // Dump an Ast node as a string 106 | func Dump(ast Ast) string { 107 | if ast == nil { 108 | return "" 109 | } 110 | name := ast.Type().Name 111 | switch name { 112 | case "ExprStmt": 113 | name = "Expr" 114 | } 115 | return dump(ast, name) 116 | } 117 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | schedule: 9 | - cron: '0 2 * * 1-5' 10 | 11 | env: 12 | GOPROXY: "https://proxy.golang.org" 13 | TAGS: "-tags=ci" 14 | COVERAGE: "-coverpkg=github.com/go-python/gpython/..." 15 | 16 | jobs: 17 | 18 | build: 19 | name: Build 20 | strategy: 21 | matrix: 22 | go-version: [1.19.x, 1.18.x] 23 | platform: [ubuntu-latest, macos-latest, windows-latest] 24 | runs-on: ${{ matrix.platform }} 25 | steps: 26 | - name: Install Go 27 | uses: actions/setup-go@v6 28 | with: 29 | go-version: ${{ matrix.go-version }} 30 | 31 | - name: Setup Git for Windows 32 | run: | 33 | git config --global core.autocrlf false 34 | git config --global core.eol lf 35 | 36 | - name: Checkout code 37 | uses: actions/checkout@v5 38 | with: 39 | fetch-depth: 1 40 | 41 | - name: Cache-Go 42 | uses: actions/cache@v4 43 | with: 44 | # In order: 45 | # * Module download cache 46 | # * Build cache (Linux) 47 | # * Build cache (Mac) 48 | # * Build cache (Windows) 49 | path: | 50 | ~/go/pkg/mod 51 | ~/.cache/go-build 52 | ~/Library/Caches/go-build 53 | '%LocalAppData%\go-build' 54 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 55 | restore-keys: | 56 | ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 57 | 58 | - name: Install Linux packages 59 | if: matrix.platform == 'ubuntu-latest' 60 | run: | 61 | sudo apt-get update 62 | sudo apt-get install -qq pkg-config python3 63 | 64 | - name: Build-Linux-32b 65 | if: matrix.platform == 'ubuntu-latest' 66 | run: | 67 | GOARCH=386 go install -v $TAGS ./... 68 | - name: Build-Linux-64b 69 | if: matrix.platform == 'ubuntu-latest' 70 | run: | 71 | GOARCH=amd64 go install -v $TAGS ./... 72 | - name: Build-Windows 73 | if: matrix.platform == 'windows-latest' 74 | run: | 75 | go install -v $TAGS ./... 76 | - name: Build-Darwin 77 | if: matrix.platform == 'macos-latest' 78 | run: | 79 | go install -v $TAGS ./... 80 | - name: Test Linux 81 | if: matrix.platform == 'ubuntu-latest' 82 | run: | 83 | GOARCH=386 go test $TAGS ./... 84 | GOARCH=amd64 go run ./ci/run-tests.go $TAGS -race $COVERAGE 85 | ## FIXME(sbinet): bring back python3.4 or upgrade gpython to python3.x 86 | ## python3 py3test.py 87 | - name: Test Windows 88 | if: matrix.platform == 'windows-latest' 89 | run: | 90 | go run ./ci/run-tests.go $TAGS -race 91 | - name: Test Darwin 92 | if: matrix.platform == 'macos-latest' 93 | run: | 94 | go run ./ci/run-tests.go $TAGS -race 95 | - name: static-check 96 | uses: dominikh/staticcheck-action@v1 97 | with: 98 | install-go: false 99 | cache-key: ${{ matrix.platform }} 100 | version: "2022.1" 101 | - name: Upload-Coverage 102 | if: matrix.platform == 'ubuntu-latest' 103 | uses: codecov/codecov-action@v3 104 | -------------------------------------------------------------------------------- /py/tests/function.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | doc="function" 6 | 7 | def fn(p): 8 | "docstring" 9 | return p+1 10 | 11 | assert fn(1) == 2 12 | 13 | # FIXME this doesn't work yet 14 | #assert fn.__doc__ == "docstring" 15 | #fn.__doc__ = "hello" 16 | #assert fn.__doc__ == "hello" 17 | 18 | assert str(type(fn)) == "" 19 | 20 | fn.x = 3 21 | assert fn.x == 3 22 | 23 | def f2(p): 24 | return p+2 25 | 26 | doc="check __code__" 27 | fn.__code__ = f2.__code__ 28 | assert fn(1) == 3 29 | try: 30 | fn.__code__ = "bad" 31 | except TypeError: 32 | pass 33 | else: 34 | assert False, "TypeError not raised" 35 | 36 | doc="check __defaults__" 37 | def f3(p=2): 38 | return p 39 | assert f3.__defaults__ == (2,) 40 | assert f3() == 2 41 | f3.__defaults__ = (10,) 42 | assert f3() == 10 43 | assert f3.__defaults__ == (10,) 44 | try: 45 | f3.__defaults__ = "bad" 46 | except TypeError: 47 | pass 48 | else: 49 | assert False, "TypeError not raised" 50 | del f3.__defaults__ 51 | assert f3.__defaults__ == None or f3.__defaults__ == () 52 | 53 | doc="check __kwdefaults__" 54 | def f4(*, b=2): 55 | return b 56 | assert f4.__kwdefaults__ == {"b":2} 57 | assert f4() == 2 58 | f4.__kwdefaults__ = {"b":10} 59 | assert f4() == 10 60 | assert f4.__kwdefaults__ == {"b":10} 61 | try: 62 | f4.__kwdefaults__ = "bad" 63 | except TypeError: 64 | pass 65 | else: 66 | assert False, "TypeError not raised" 67 | del f4.__kwdefaults__ 68 | assert f4.__kwdefaults__ == None or f4.__kwdefaults__ == {} 69 | 70 | doc="check __annotations__" 71 | def f5(a: "potato") -> "sausage": 72 | pass 73 | assert f5.__annotations__ == {'a': 'potato', 'return': 'sausage'} 74 | f5.__annotations__ = {'a': 'potato', 'return': 'SAUSAGE'} 75 | assert f5.__annotations__ == {'a': 'potato', 'return': 'SAUSAGE'} 76 | try: 77 | f5.__annotations__ = "bad" 78 | except TypeError: 79 | pass 80 | else: 81 | assert False, "TypeError not raised" 82 | del f5.__annotations__ 83 | assert f5.__annotations__ == None or f5.__annotations__ == {} 84 | 85 | doc="check __dict__" 86 | def f6(): 87 | pass 88 | assert f6.__dict__ == {} 89 | f6.__dict__ = {'a': 'potato'} 90 | assert f6.__dict__ == {'a': 'potato'} 91 | try: 92 | f6.__dict__ = "bad" 93 | except TypeError: 94 | pass 95 | else: 96 | assert False, "TypeError not raised" 97 | try: 98 | del f6.__dict__ 99 | except (TypeError, AttributeError): 100 | pass 101 | else: 102 | assert False, "Error not raised" 103 | 104 | doc="check __name__" 105 | def f7(): 106 | pass 107 | assert f7.__name__ == "f7" 108 | f7.__name__ = "new_name" 109 | assert f7.__name__ == "new_name" 110 | try: 111 | f7.__name__ = 1 112 | except TypeError: 113 | pass 114 | else: 115 | assert False, "TypeError not raised" 116 | 117 | doc="check __qualname__" 118 | def f8(): 119 | pass 120 | assert f8.__qualname__ == "f8" 121 | f8.__qualname__ = "new_qualname" 122 | assert f8.__qualname__ == "new_qualname" 123 | try: 124 | f8.__qualname__ = 1 125 | except TypeError: 126 | pass 127 | else: 128 | assert False, "TypeError not raised" 129 | 130 | doc="finished" 131 | -------------------------------------------------------------------------------- /py/tests/range.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | doc="range" 6 | a = range(255) 7 | b = [e for e in a] 8 | assert len(a) == len(b) 9 | a = range(5, 100, 5) 10 | b = [e for e in a] 11 | assert len(a) == len(b) 12 | a = range(100 ,0, 1) 13 | b = [e for e in a] 14 | assert len(a) == len(b) 15 | 16 | a = range(100, 0, -1) 17 | b = [e for e in a] 18 | assert len(a) == 100 19 | assert len(b) == 100 20 | 21 | doc="range_get_item" 22 | a = range(3) 23 | assert a[2] == 2 24 | assert a[1] == 1 25 | assert a[0] == 0 26 | assert a[-1] == 2 27 | assert a[-2] == 1 28 | assert a[-3] == 0 29 | 30 | b = range(0, 10, 2) 31 | assert b[4] == 8 32 | assert b[3] == 6 33 | assert b[2] == 4 34 | assert b[1] == 2 35 | assert b[0] == 0 36 | assert b[-4] == 2 37 | assert b[-3] == 4 38 | assert b[-2] == 6 39 | assert b[-1] == 8 40 | 41 | doc="range_eq" 42 | assert range(10) == range(0, 10) 43 | assert not range(10) == 3 44 | assert range(20) != range(10) 45 | assert range(100, 200, 1) == range(100, 200) 46 | assert range(0, 10, 3) == range(0, 12, 3) 47 | assert range(2000, 100) == range(3, 1) 48 | assert range(0, 10, -3) == range(0, 12, -3) 49 | assert not range(0, 20, 2) == range(0, 20, 4) 50 | try: 51 | range('3', 10) == range(2) 52 | except TypeError: 53 | pass 54 | else: 55 | assert False, "TypeError not raised" 56 | 57 | doc="range_ne" 58 | assert range(10, 0, -3) != range(12, 0, -3) 59 | assert range(10) != 3 60 | assert not range(100, 200, 1) != range(100, 200) 61 | assert range(0, 10) != range(0, 12) 62 | assert range(0, 10) != range(0, 10, 2) 63 | assert range(0, 20, 2) != range(0, 21, 2) 64 | assert range(0, 20, 2) != range(0, 20, 4) 65 | assert not range(0, 20, 3) != range(0, 20, 3) 66 | try: 67 | range('3', 10) != range(2) 68 | except TypeError: 69 | pass 70 | else: 71 | assert False, "TypeError not raised" 72 | 73 | doc="range_str" 74 | assert str(range(10)) == 'range(0, 10)' 75 | assert str(range(10, 0, 3)) == 'range(10, 0, 3)' 76 | assert str(range(0, 3)) == 'range(0, 3)' 77 | assert str(range(10, 3, -2)) == 'range(10, 3, -2)' 78 | 79 | doc="range_slice" 80 | a = range(10) 81 | assert a[::-1][0] == 9 82 | assert a[::-1][9] == 0 83 | assert a[0:3][0] == 0 84 | assert a[0:3][2] == 2 85 | assert a[-3:10][0] == 7 86 | assert a[-100:13][0] == 0 87 | assert a[-100:13][9] == 9 88 | 89 | try: 90 | a[0:3][3] 91 | except IndexError: 92 | pass 93 | else: 94 | assert False, "IndexError not raised" 95 | try: 96 | a[100:13][0] 97 | except IndexError: 98 | pass 99 | else: 100 | assert False, "IndexError not raised" 101 | try: 102 | a[0:3:0] 103 | except ValueError: 104 | pass 105 | else: 106 | assert False, "ValueError not raised" 107 | 108 | doc="range_index" 109 | class Index: 110 | def __index__(self): 111 | return 1 112 | 113 | a = range(10) 114 | b = Index() 115 | assert a[b] == 1 116 | assert a[b:10] == a[1:10] 117 | assert a[10:b:-1] == a[10:1:-1] 118 | 119 | class NonIntegerIndex: 120 | def __index__(self): 121 | return 1.1 122 | 123 | a = range(10) 124 | b = NonIntegerIndex() 125 | try: 126 | a[b] 127 | except TypeError: 128 | pass 129 | else: 130 | assert False, "TypeError not raised" 131 | 132 | try: 133 | a[b:10] 134 | except TypeError: 135 | pass 136 | else: 137 | assert False, "TypeError not raised" 138 | 139 | doc="finished" 140 | -------------------------------------------------------------------------------- /py/tests/bytes.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | doc="str" 6 | assert str(b"") == "b''" 7 | assert str(b"hello") == r"b'hello'" 8 | assert str(rb"""hel"lo""") == r"""b'hel"lo'""" 9 | assert str(b"""he 10 | llo""") == r"""b'he\nllo'""" 11 | assert str(rb"""hel'lo""") == r'''b"hel'lo"''' 12 | assert str(b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff') == r"""b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff'""" 13 | 14 | doc="repr" 15 | assert repr(b"") == "b''" 16 | assert repr(b"hello") == r"b'hello'" 17 | assert repr(rb"""hel"lo""") == r"""b'hel"lo'""" 18 | assert repr(b"""he 19 | llo""") == r"""b'he\nllo'""" 20 | assert repr(rb"""hel'lo""") == r'''b"hel'lo"''' 21 | assert repr(b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff') == r"""b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff'""" 22 | 23 | doc="finished" 24 | -------------------------------------------------------------------------------- /repl/repl_test.go: -------------------------------------------------------------------------------- 1 | package repl 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "testing" 7 | 8 | // import required modules 9 | _ "github.com/go-python/gpython/stdlib" 10 | ) 11 | 12 | type replTest struct { 13 | prompt string 14 | out string 15 | } 16 | 17 | // SetPrompt sets the current terminal prompt 18 | func (rt *replTest) SetPrompt(prompt string) { 19 | rt.prompt = prompt 20 | } 21 | 22 | // Print prints the output 23 | func (rt *replTest) Print(out string) { 24 | rt.out = out 25 | } 26 | 27 | func (rt *replTest) assert(t *testing.T, what, wantPrompt, wantOut string) { 28 | t.Helper() 29 | if rt.prompt != wantPrompt { 30 | t.Errorf("%s: Prompt wrong:\ngot= %q\nwant=%q", what, rt.prompt, wantPrompt) 31 | } 32 | if rt.out != wantOut { 33 | t.Errorf("%s: Output wrong:\ngot= %q\nwant=%q", what, rt.out, wantOut) 34 | } 35 | rt.out = "" 36 | } 37 | 38 | func TestREPL(t *testing.T) { 39 | r := New(nil) 40 | rt := &replTest{} 41 | r.SetUI(rt) 42 | 43 | rt.assert(t, "init", NormalPrompt, "") 44 | 45 | r.Run("") 46 | rt.assert(t, "empty", NormalPrompt, "") 47 | 48 | r.Run("1+2") 49 | rt.assert(t, "1+2", NormalPrompt, "3") 50 | 51 | // FIXME this output goes to Stderr and Stdout 52 | r.Run("aksfjakf") 53 | rt.assert(t, "unbound", NormalPrompt, "") 54 | 55 | r.Run("sum = 0") 56 | rt.assert(t, "multi#1", NormalPrompt, "") 57 | r.Run("for i in range(10):") 58 | rt.assert(t, "multi#2", ContinuationPrompt, "") 59 | r.Run(" sum += i") 60 | rt.assert(t, "multi#3", ContinuationPrompt, "") 61 | r.Run("") 62 | rt.assert(t, "multi#4", NormalPrompt, "") 63 | r.Run("sum") 64 | rt.assert(t, "multi#5", NormalPrompt, "45") 65 | 66 | r.Run("if") 67 | rt.assert(t, "compileError", NormalPrompt, "Compile error: \n File \"\", line 1, offset 2\n if\n\n\nSyntaxError: 'invalid syntax'") 68 | 69 | // test comments in the REPL work properly 70 | r.Run("# this is a comment") 71 | rt.assert(t, "comment", NormalPrompt, "") 72 | r.Run("a = 42") 73 | rt.assert(t, "comment continuation", NormalPrompt, "") 74 | r.Run("a") 75 | rt.assert(t, "comment check", NormalPrompt, "42") 76 | } 77 | 78 | func TestCompleter(t *testing.T) { 79 | r := New(nil) 80 | rt := &replTest{} 81 | r.SetUI(rt) 82 | 83 | for _, test := range []struct { 84 | line string 85 | pos int 86 | wantHead string 87 | wantCompletions []string 88 | wantTail string 89 | }{ 90 | { 91 | line: "di", 92 | pos: 2, 93 | wantHead: "", 94 | wantCompletions: []string{"dict", "divmod"}, 95 | wantTail: "", 96 | }, 97 | { 98 | line: "div", 99 | pos: 3, 100 | wantHead: "", 101 | wantCompletions: []string{"divmod"}, 102 | wantTail: "", 103 | }, 104 | { 105 | line: "doodle", 106 | pos: 6, 107 | wantHead: "", 108 | wantCompletions: nil, 109 | wantTail: "", 110 | }, 111 | { 112 | line: "divmod divm", 113 | pos: 9, 114 | wantHead: "divmod ", 115 | wantCompletions: []string{"divmod"}, 116 | wantTail: "vm", 117 | }, 118 | } { 119 | t.Run(fmt.Sprintf("line=%q,pos=%d)", test.line, test.pos), func(t *testing.T) { 120 | gotHead, gotCompletions, gotTail := r.Completer(test.line, test.pos) 121 | if test.wantHead != gotHead { 122 | t.Errorf("invalid head:\ngot= %q\nwant=%q", gotHead, test.wantHead) 123 | } 124 | if !reflect.DeepEqual(test.wantCompletions, gotCompletions) { 125 | t.Errorf("invalid completions:\ngot= %#v\nwant=%#v", gotCompletions, test.wantCompletions) 126 | } 127 | if test.wantTail != gotTail { 128 | t.Errorf("invalid tail:\ngot= %q\nwant=%q", gotTail, test.wantTail) 129 | } 130 | }) 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /examples/pi_chudnovsky_bs.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | """ 6 | Python3 program to calculate Pi using python long integers, binary 7 | splitting and the Chudnovsky algorithm 8 | 9 | See: http://www.craig-wood.com/nick/articles/ FIXME for explanation 10 | 11 | Nick Craig-Wood 12 | """ 13 | 14 | import math 15 | from time import time 16 | 17 | def sqrt(n, one): 18 | """ 19 | Return the square root of n as a fixed point number with the one 20 | passed in. It uses a second order Newton-Raphson convgence. This 21 | doubles the number of significant figures on each iteration. 22 | """ 23 | # Use floating point arithmetic to make an initial guess 24 | floating_point_precision = 10**16 25 | n_float = float((n * floating_point_precision) // one) / floating_point_precision 26 | x = (int(floating_point_precision * math.sqrt(n_float)) * one) // floating_point_precision 27 | n_one = n * one 28 | while 1: 29 | x_old = x 30 | x = (x + n_one // x) // 2 31 | if x == x_old: 32 | break 33 | return x 34 | 35 | 36 | def pi_chudnovsky_bs(digits): 37 | """ 38 | Compute int(pi * 10**digits) 39 | 40 | This is done using Chudnovsky's series with binary splitting 41 | """ 42 | C = 640320 43 | C3_OVER_24 = C**3 // 24 44 | def bs(a, b): 45 | """ 46 | Computes the terms for binary splitting the Chudnovsky infinite series 47 | 48 | a(a) = +/- (13591409 + 545140134*a) 49 | p(a) = (6*a-5)*(2*a-1)*(6*a-1) 50 | b(a) = 1 51 | q(a) = a*a*a*C3_OVER_24 52 | 53 | returns P(a,b), Q(a,b) and T(a,b) 54 | """ 55 | if b - a == 1: 56 | # Directly compute P(a,a+1), Q(a,a+1) and T(a,a+1) 57 | if a == 0: 58 | Pab = Qab = 1 59 | else: 60 | Pab = (6*a-5)*(2*a-1)*(6*a-1) 61 | Qab = a*a*a*C3_OVER_24 62 | Tab = Pab * (13591409 + 545140134*a) # a(a) * p(a) 63 | if a & 1: 64 | Tab = -Tab 65 | else: 66 | # Recursively compute P(a,b), Q(a,b) and T(a,b) 67 | # m is the midpoint of a and b 68 | m = (a + b) // 2 69 | # Recursively calculate P(a,m), Q(a,m) and T(a,m) 70 | Pam, Qam, Tam = bs(a, m) 71 | # Recursively calculate P(m,b), Q(m,b) and T(m,b) 72 | Pmb, Qmb, Tmb = bs(m, b) 73 | # Now combine 74 | Pab = Pam * Pmb 75 | Qab = Qam * Qmb 76 | Tab = Qmb * Tam + Pam * Tmb 77 | return Pab, Qab, Tab 78 | # how many terms to compute 79 | DIGITS_PER_TERM = math.log10(C3_OVER_24/6/2/6) 80 | N = int(digits/DIGITS_PER_TERM + 1) 81 | # Calclate P(0,N) and Q(0,N) 82 | P, Q, T = bs(0, N) 83 | one = 10**digits 84 | sqrtC = sqrt(10005*one, one) 85 | return (Q*426880*sqrtC) // T 86 | 87 | # The last 5 digits or pi for various numbers of digits 88 | check_digits = ( 89 | (100, 70679), 90 | (1000, 1989), 91 | (10000, 75678), 92 | (100000, 24646), 93 | (1000000, 58151), 94 | (10000000, 55897), 95 | ) 96 | 97 | if __name__ == "__main__": 98 | digits = 100 99 | pi = pi_chudnovsky_bs(digits) 100 | print(str(pi)) 101 | for digits, check in check_digits: 102 | start =time() 103 | pi = pi_chudnovsky_bs(digits) 104 | print("chudnovsky_gmpy_bs: digits",digits,"time",time()-start) 105 | last_five_digits = pi % 100000 106 | if check == last_five_digits: 107 | print("Last 5 digits %05d OK" % last_five_digits) 108 | else: 109 | print("Last 5 digits %05d wrong should be %05d" % (last_five_digits, check)) 110 | -------------------------------------------------------------------------------- /vm/tests/exceptions.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | # Test exceptions 6 | 7 | doc="except" 8 | ok = False 9 | try: 10 | raise ValueError 11 | except ValueError: 12 | ok = True 13 | assert ok 14 | 15 | doc="except as" 16 | ok = False 17 | try: 18 | raise ValueError 19 | except ValueError as e: 20 | ok = True 21 | assert ok 22 | 23 | doc="except (a, b) as" 24 | ok = False 25 | try: 26 | raise ValueError 27 | except (IOError, ValueError) as e: 28 | ok = True 29 | assert ok 30 | 31 | doc="raise exception instance" 32 | ok = False 33 | try: 34 | raise ValueError("Potato") 35 | except (IOError, ValueError) as e: 36 | ok = True 37 | assert ok 38 | 39 | doc="exception hierarchy" 40 | # FIXME doesn't work because IsSubtype is broken ValueError.IsSubtype(Exception) == false 41 | # ok = False 42 | # try: 43 | # raise ValueError("potato") 44 | # except Exception: 45 | # ok = True 46 | # assert ok 47 | 48 | doc="exception match" 49 | ok = False 50 | try: 51 | raise ValueError 52 | except IOError: 53 | assert False, "Not expecting IO Error" 54 | except ValueError: 55 | ok = True 56 | assert ok 57 | 58 | doc="no exception" 59 | ok = False 60 | try: 61 | pass 62 | except ValueError: 63 | assert False, "Not expecting ValueError" 64 | else: 65 | ok = True 66 | assert ok 67 | 68 | doc="nested" 69 | ok = False 70 | try: 71 | try: 72 | raise ValueError("potato") 73 | except IOError as e: 74 | assert False, "Not expecting IOError" 75 | else: 76 | assert False, "Expecting ValueError" 77 | except ValueError: 78 | ok = True 79 | else: 80 | assert False, "Expecting ValueError (outer)" 81 | assert ok 82 | 83 | doc="nested #2" 84 | ok1 = False 85 | ok2 = False 86 | try: 87 | try: 88 | raise IOError("potato") 89 | except IOError as e: 90 | ok1 = True 91 | else: 92 | assert False, "Expecting ValueError" 93 | except ValueError: 94 | assert False, "Expecting IOError" 95 | except IOError: 96 | assert False, "Expecting IOError" 97 | else: 98 | ok2 = True 99 | assert ok 100 | 101 | doc="re-raise" 102 | ok1 = False 103 | ok2 = False 104 | try: 105 | try: 106 | raise ValueError("potato") 107 | except ValueError as e: 108 | ok2 = True 109 | raise 110 | else: 111 | assert False, "Expecting ValueError (inner)" 112 | except ValueError: 113 | ok1 = True 114 | else: 115 | assert False, "Expecting ValueError (outer)" 116 | assert ok1 and ok2 117 | 118 | doc="try/finally" 119 | ok1 = False 120 | ok2 = False 121 | ok3 = False 122 | try: 123 | try: 124 | ok1 = True 125 | finally: 126 | ok2 = True 127 | except ValueError: 128 | assert False, "Not expecting ValueError (outer)" 129 | else: 130 | ok3 = True 131 | assert ok1 and ok2 and ok3 132 | 133 | doc="try/finally #2" 134 | ok1 = False 135 | ok2 = False 136 | try: 137 | try: 138 | raise ValueError() 139 | finally: 140 | ok1 = True 141 | except ValueError: 142 | ok2 = True 143 | else: 144 | assert False, "Expecting ValueError (outer)" 145 | assert ok1 and ok2 146 | 147 | doc="internal exception" 148 | ok = False 149 | try: 150 | print(1/0) 151 | except ZeroDivisionError: 152 | ok = True 153 | assert ok 154 | 155 | doc = "raise in else" 156 | ok = False 157 | try: 158 | try: 159 | pass 160 | except NameError as e: 161 | pass 162 | else: 163 | raise ValueError 164 | except ValueError: 165 | ok = True 166 | assert ok, "ValueError not raised" 167 | 168 | doc = "exception repr" 169 | repr(ValueError()) == "ValueError()" 170 | repr(ValueError(1)) == "ValueError(1)" 171 | repr(ValueError(1, 2, 3)) == "ValueError(1, 2, 3)" 172 | repr(ValueError("failed")) == 'ValueError("failed")' 173 | 174 | doc = "finished" 175 | -------------------------------------------------------------------------------- /stdlib/string/string.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package string provides the implementation of the python's 'string' module. 6 | package string 7 | 8 | import ( 9 | "strings" 10 | 11 | "github.com/go-python/gpython/py" 12 | ) 13 | 14 | func init() { 15 | py.RegisterModule(&py.ModuleImpl{ 16 | Info: py.ModuleInfo{ 17 | Name: "string", 18 | Doc: module_doc, 19 | }, 20 | Methods: []*py.Method{ 21 | py.MustNewMethod("capwords", capwords, 0, capwords_doc), 22 | }, 23 | Globals: py.StringDict{ 24 | "whitespace": whitespace, 25 | "ascii_lowercase": ascii_lowercase, 26 | "ascii_uppercase": ascii_uppercase, 27 | "ascii_letters": ascii_letters, 28 | "digits": digits, 29 | "hexdigits": hexdigits, 30 | "octdigits": octdigits, 31 | "punctuation": punctuation, 32 | "printable": printable, 33 | }, 34 | }) 35 | } 36 | 37 | const module_doc = `A collection of string constants. 38 | 39 | Public module variables: 40 | 41 | whitespace -- a string containing all ASCII whitespace 42 | ascii_lowercase -- a string containing all ASCII lowercase letters 43 | ascii_uppercase -- a string containing all ASCII uppercase letters 44 | ascii_letters -- a string containing all ASCII letters 45 | digits -- a string containing all ASCII decimal digits 46 | hexdigits -- a string containing all ASCII hexadecimal digits 47 | octdigits -- a string containing all ASCII octal digits 48 | punctuation -- a string containing all ASCII punctuation characters 49 | printable -- a string containing all ASCII characters considered printable 50 | ` 51 | 52 | var ( 53 | whitespace = py.String(" \t\n\r\x0b\x0c") 54 | ascii_lowercase = py.String("abcdefghijklmnopqrstuvwxyz") 55 | ascii_uppercase = py.String("ABCDEFGHIJKLMNOPQRSTUVWXYZ") 56 | ascii_letters = ascii_lowercase + ascii_uppercase 57 | digits = py.String("0123456789") 58 | hexdigits = py.String("0123456789abcdefABCDEF") 59 | octdigits = py.String("01234567") 60 | punctuation = py.String("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~") 61 | printable = py.String("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c") 62 | ) 63 | 64 | const capwords_doc = `capwords(s [,sep]) -> string 65 | 66 | Split the argument into words using split, capitalize each 67 | word using capitalize, and join the capitalized words using 68 | join. If the optional second argument sep is absent or None, 69 | runs of whitespace characters are replaced by a single space 70 | and leading and trailing whitespace are removed, otherwise 71 | sep is used to split and join the words.` 72 | 73 | func capwords(self py.Object, args py.Tuple, kwargs py.StringDict) (py.Object, error) { 74 | var ( 75 | pystr py.Object 76 | pysep py.Object = py.None 77 | ) 78 | err := py.ParseTupleAndKeywords(args, kwargs, "s|z", []string{"s", "sep"}, &pystr, &pysep) 79 | if err != nil { 80 | return nil, err 81 | } 82 | 83 | pystr = py.String(strings.ToLower(string(pystr.(py.String)))) 84 | pyvs, err := pystr.(py.String).Split(py.Tuple{pysep}, nil) 85 | if err != nil { 86 | return nil, err 87 | } 88 | 89 | var ( 90 | lst = pyvs.(*py.List).Items 91 | vs = make([]string, len(lst)) 92 | sep = "" 93 | title = func(s string) string { 94 | if s == "" { 95 | return s 96 | } 97 | return strings.ToUpper(s[:1]) + s[1:] 98 | } 99 | ) 100 | 101 | switch pysep { 102 | case py.None: 103 | for i := range vs { 104 | v := string(lst[i].(py.String)) 105 | vs[i] = title(strings.Trim(v, string(whitespace))) 106 | } 107 | sep = " " 108 | default: 109 | sep = string(pysep.(py.String)) 110 | for i := range vs { 111 | v := string(lst[i].(py.String)) 112 | vs[i] = title(v) 113 | } 114 | } 115 | 116 | return py.String(strings.Join(vs, sep)), nil 117 | } 118 | -------------------------------------------------------------------------------- /examples/multi-context/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | "runtime" 11 | "strings" 12 | "sync" 13 | "time" 14 | 15 | // This initializes gpython for runtime execution and is critical. 16 | // It defines forward-declared symbols and registers native built-in modules, such as sys and time. 17 | _ "github.com/go-python/gpython/stdlib" 18 | 19 | // This is the primary import for gpython. 20 | // It contains all symbols needed to fully compile and run python. 21 | "github.com/go-python/gpython/py" 22 | ) 23 | 24 | func main() { 25 | 26 | // The total job count implies a fixed amount of work. 27 | // The number of workers is how many py.Context (in concurrent goroutines) to pull jobs off the queue. 28 | // One worker does all the work serially while N number of workers will (ideally) divides up. 29 | totalJobs := 20 30 | 31 | for i := 0; i < 10; i++ { 32 | numWorkers := i + 1 33 | elapsed := RunMultiPi(numWorkers, totalJobs) 34 | fmt.Printf("=====> %2d worker(s): %v\n\n", numWorkers, elapsed) 35 | 36 | // Give each trial a fresh start 37 | runtime.GC() 38 | } 39 | } 40 | 41 | var jobScript = ` 42 | pi = chud.pi_chudnovsky_bs(numDigits) 43 | last_5 = pi % 100000 44 | print("%s: last 5 digits of %d is %d (job #%0d)" % (WORKER_ID, numDigits, last_5, jobID)) 45 | ` 46 | 47 | var jobSrcTemplate = ` 48 | import pi_chudnovsky_bs as chud 49 | 50 | WORKER_ID = "{{WORKER_ID}}" 51 | 52 | print("%s ready!" % (WORKER_ID)) 53 | ` 54 | 55 | type worker struct { 56 | name string 57 | ctx py.Context 58 | main *py.Module 59 | job *py.Code 60 | } 61 | 62 | func (w *worker) compileTemplate(pySrc string) { 63 | pySrc = strings.Replace(pySrc, "{{WORKER_ID}}", w.name, -1) 64 | 65 | mainImpl := py.ModuleImpl{ 66 | CodeSrc: pySrc, 67 | } 68 | 69 | var err error 70 | w.main, err = w.ctx.ModuleInit(&mainImpl) 71 | if err != nil { 72 | log.Fatal(err) 73 | } 74 | } 75 | 76 | func RunMultiPi(numWorkers, numTimes int) time.Duration { 77 | var workersRunning sync.WaitGroup 78 | 79 | fmt.Printf("Starting %d worker(s) to calculate %d jobs...\n", numWorkers, numTimes) 80 | 81 | jobPipe := make(chan int) 82 | go func() { 83 | for i := 0; i < numTimes; i++ { 84 | jobPipe <- i + 1 85 | } 86 | close(jobPipe) 87 | }() 88 | 89 | // Note that py.Code can be shared (accessed concurrently) since it is an inherently read-only object 90 | jobCode, err := py.Compile(jobScript, "", py.ExecMode, 0, true) 91 | if err != nil { 92 | log.Fatal("jobScript failed to comple") 93 | } 94 | 95 | workers := make([]worker, numWorkers) 96 | for i := 0; i < numWorkers; i++ { 97 | 98 | opts := py.DefaultContextOpts() 99 | 100 | // Make sure our import statement will find pi_chudnovsky_bs 101 | opts.SysPaths = append(opts.SysPaths, "..") 102 | 103 | workers[i] = worker{ 104 | name: fmt.Sprintf("Worker #%d", i+1), 105 | ctx: py.NewContext(opts), 106 | job: jobCode, 107 | } 108 | 109 | workersRunning.Add(1) 110 | } 111 | 112 | startTime := time.Now() 113 | 114 | for i := range workers { 115 | w := workers[i] 116 | go func() { 117 | 118 | // Compiling can be concurrent since there is no associated py.Context 119 | w.compileTemplate(jobSrcTemplate) 120 | 121 | for jobID := range jobPipe { 122 | numDigits := 100000 123 | if jobID%2 == 0 { 124 | numDigits *= 10 125 | } 126 | py.SetAttrString(w.main.Globals, "numDigits", py.Int(numDigits)) 127 | py.SetAttrString(w.main.Globals, "jobID", py.Int(jobID)) 128 | w.ctx.RunCode(jobCode, w.main.Globals, w.main.Globals, nil) 129 | } 130 | workersRunning.Done() 131 | 132 | // This drives modules being able to perform cleanup and release resources 133 | w.ctx.Close() 134 | }() 135 | } 136 | 137 | workersRunning.Wait() 138 | 139 | return time.Since(startTime) 140 | } 141 | -------------------------------------------------------------------------------- /stdlib/tempfile/testdata/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | import tempfile 6 | import os 7 | 8 | print("test tempfile") 9 | 10 | if not tempfile.tempdir is None: 11 | print("tempfile.tempdir is not None: %s" % (tempfile.tempdir,)) 12 | else: 13 | print("tempfile.tempdir is None [OK]") 14 | 15 | v = tempfile.gettempdir() 16 | if type(v) != type(""): 17 | print("tempfile.gettempdir() returned %s (type=%s)" % (v, type(v))) 18 | 19 | v = tempfile.gettempdirb() 20 | if type(v) != type(b""): 21 | print("tempfile.gettempdirb() returned %s (type=%s)" % (v, type(v))) 22 | 23 | ## mkdtemp 24 | try: 25 | tmp = tempfile.mkdtemp() 26 | os.rmdir(tmp) 27 | print("mkdtemp() [OK]") 28 | except Exception as e: 29 | print("could not create tmp dir w/ mkdtemp(): %s" % e) 30 | 31 | try: 32 | tmp = tempfile.mkdtemp(prefix="prefix-", suffix="-suffix") 33 | os.rmdir(tmp) 34 | print("mkdtemp(prefix='prefix-', suffix='-suffix') [OK]") 35 | except Exception as e: 36 | print("could not create tmp dir w/ mkdtemp(prefix='prefix-', suffix='-suffix'): %s" % e) 37 | 38 | try: 39 | top = tempfile.mkdtemp(prefix="prefix-", suffix="-suffix") 40 | tmp = tempfile.mkdtemp(prefix="prefix-", suffix="-suffix", dir=top) 41 | os.rmdir(tmp) 42 | os.rmdir(top) 43 | print("mkdtemp(prefix='prefix-', suffix='-suffix', dir=top) [OK]") 44 | except Exception as e: 45 | print("could not create tmp dir w/ mkdtemp(prefix='prefix-', suffix='-suffix', dir=top): %s" % e) 46 | 47 | try: 48 | top = tempfile.mkdtemp(prefix="prefix-", suffix="-suffix") 49 | tmp = tempfile.mkdtemp(prefix="prefix-", suffix="-suffix", dir=top) 50 | os.removedirs(top) 51 | print("mkdtemp(prefix='prefix-', suffix='-suffix', dir=top) [OK]") 52 | except Exception as e: 53 | print("could not create tmp dir w/ mkdtemp(prefix='prefix-', suffix='-suffix', dir=top): %s" % e) 54 | 55 | try: 56 | tmp = tempfile.mkdtemp(prefix=b"prefix-", suffix="-suffix") 57 | print("missing exception!") 58 | os.rmdir(tmp) 59 | except TypeError as e: 60 | print("caught: %s [OK]" % e) 61 | except Exception as e: 62 | print("INVALID error caught: %s" % e) 63 | 64 | def remove(fd, name): 65 | os.close(fd) 66 | os.remove(name) 67 | 68 | ## mkstemp 69 | try: 70 | fd, tmp = tempfile.mkstemp() 71 | remove(fd, tmp) 72 | print("mkstemp() [OK]") 73 | except Exception as e: 74 | print("could not create tmp dir w/ mkstemp(): %s" % e) 75 | 76 | try: 77 | fd, tmp = tempfile.mkstemp(prefix="prefix-", suffix="-suffix") 78 | remove(fd, tmp) 79 | print("mkstemp(prefix='prefix-', suffix='-suffix') [OK]") 80 | except Exception as e: 81 | print("could not create tmp dir w/ mkstemp(prefix='prefix-', suffix='-suffix'): %s" % e) 82 | 83 | try: 84 | top = tempfile.mkdtemp(prefix="prefix-", suffix="-suffix") 85 | fd, tmp = tempfile.mkstemp(prefix="prefix-", suffix="-suffix", dir=top) 86 | remove(fd, tmp) 87 | os.remove(top) 88 | print("mkstemp(prefix='prefix-', suffix='-suffix', dir=top) [OK]") 89 | except Exception as e: 90 | print("could not create tmp dir w/ mkstemp(prefix='prefix-', suffix='-suffix', dir=top): %s" % e) 91 | 92 | try: 93 | top = tempfile.mkdtemp(prefix="prefix-", suffix="-suffix") 94 | fd, tmp = tempfile.mkstemp(prefix="prefix-", suffix="-suffix", dir=top) 95 | os.fdopen(fd).close() ## needed on Windows. 96 | os.removedirs(top) 97 | print("mkstemp(prefix='prefix-', suffix='-suffix', dir=top) [OK]") 98 | except Exception as e: 99 | print("could not create tmp dir w/ mkstemp(prefix='prefix-', suffix='-suffix', dir=top): %s" % e) 100 | 101 | try: 102 | fd, tmp = tempfile.mkstemp(prefix=b"prefix-", suffix="-suffix") 103 | print("missing exception!") 104 | remove(fd, tmp) 105 | except TypeError as e: 106 | print("caught: %s [OK]" % e) 107 | except Exception as e: 108 | print("INVALID error caught: %s" % e) 109 | 110 | print("OK") 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gpython 2 | 3 | [![Build Status](https://github.com/go-python/gpython/workflows/CI/badge.svg)](https://github.com/go-python/gpython/actions) 4 | [![codecov](https://codecov.io/gh/go-python/gpython/branch/main/graph/badge.svg)](https://codecov.io/gh/go-python/gpython) 5 | [![GoDoc](https://godoc.org/github.com/go-python/gpython?status.svg)](https://godoc.org/github.com/go-python/gpython) 6 | [![License](https://img.shields.io/badge/License-BSD--3-blue.svg)](https://github.com/go-python/gpython/blob/main/LICENSE) 7 | 8 | gpython is a part re-implementation, part port of the Python 3.4 9 | interpreter in Go. Although there are many areas of improvement, 10 | it stands as an noteworthy achievement in capability and potential. 11 | 12 | gpython includes: 13 | 14 | * lexer, parser, and compiler 15 | * runtime and high-level convenience functions 16 | * multi-context interpreter instancing 17 | * easy embedding into your Go application 18 | * interactive mode (REPL) ([try online!](https://gpython.org)) 19 | 20 | 21 | gpython does not include many python modules as many of the core 22 | modules are written in C not python. The converted modules are: 23 | 24 | * builtins 25 | * marshal 26 | * math 27 | * time 28 | * sys 29 | 30 | ## Install 31 | 32 | Download directly from the [releases page](https://github.com/go-python/gpython/releases) 33 | 34 | Or if you have Go installed: 35 | 36 | go install github.com/go-python/gpython 37 | 38 | ## Objectives 39 | 40 | gpython started as an experiment to investigate how hard 41 | porting Python to Go might be. It turns out that all those C modules 42 | are a significant barrier to making gpython a complete replacement 43 | to CPython. 44 | 45 | However, to those who want to embed a highly popular and known language 46 | into their Go application, gpython could be a great choice over less 47 | capable (or lesser known) alternatives. 48 | 49 | ## Status 50 | 51 | gpython currently: 52 | - Parses all the code in the Python 3.4 distribution 53 | - Runs Python 3 for the modules that are currently supported 54 | - Supports concurrent multi-interpreter ("multi-context") execution 55 | 56 | Speed hasn't been a goal of the conversions however it runs pystone at 57 | about 20% of the speed of CPython. A [π computation test](https://github.com/go-python/gpython/tree/main/examples/pi_chudnovsky_bs.py) runs quicker under 58 | gpython as the Go long integer primitives are likely faster than the 59 | Python ones. 60 | 61 | @ncw started gpython in 2013 and work on is sporadic. If you or someone 62 | you know would be interested to take it futher, it would be much appreciated. 63 | 64 | ## Getting Started 65 | 66 | The [embedding example](https://github.com/go-python/gpython/tree/main/examples/embedding) demonstrates how to 67 | easily embed and invoke gpython from any Go application. 68 | 69 | Of interest, gpython is able to run multiple interpreter instances simultaneously, 70 | allowing you to embed gpython naturally into your Go application. This makes it 71 | possible to use gpython in a server situation where complete interpreter 72 | independence is paramount. See this in action in the [multi-context example](https://github.com/go-python/gpython/tree/main/examples/multi-context). 73 | 74 | If you are looking to get involved, a light and easy place to start is adding more convenience functions to [py/util.go](https://github.com/go-python/gpython/tree/main/py/util.go). See [notes.txt](https://github.com/go-python/gpython/blob/main/notes.txt) for bigger ideas. 75 | 76 | 77 | ## Other Projects of Interest 78 | 79 | * [grumpy](https://github.com/grumpyhome/grumpy) - a python to go transpiler 80 | 81 | ## Community 82 | 83 | You can chat with the go-python community (or which gpython is part) 84 | at [go-python@googlegroups.com](https://groups.google.com/forum/#!forum/go-python) 85 | or on the [Gophers Slack](https://gophers.slack.com/) in the `#go-python` channel. 86 | 87 | ## License 88 | 89 | This is licensed under the MIT licence, however it contains code which 90 | was ported fairly directly directly from the CPython source code under 91 | the [PSF LICENSE](https://github.com/python/cpython/blob/main/LICENSE). 92 | -------------------------------------------------------------------------------- /repl/cli/cli.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Read Eval Print Loop for CLI 6 | package cli 7 | 8 | import ( 9 | "fmt" 10 | "io" 11 | "os" 12 | "os/user" 13 | "path/filepath" 14 | 15 | "github.com/go-python/gpython/repl" 16 | "github.com/peterh/liner" 17 | ) 18 | 19 | const HistoryFileName = ".gpyhistory" 20 | 21 | // homeDirectory finds the home directory or returns "" 22 | func homeDirectory() string { 23 | usr, err := user.Current() 24 | if err == nil { 25 | return usr.HomeDir 26 | } 27 | // Fall back to reading $HOME - work around user.Current() not 28 | // working for cross compiled binaries on OSX. 29 | // https://github.com/golang/go/issues/6376 30 | return os.Getenv("HOME") 31 | } 32 | 33 | // Holds state for readline services 34 | type readline struct { 35 | *liner.State 36 | repl *repl.REPL 37 | historyFile string 38 | prompt string 39 | } 40 | 41 | // newReadline creates a new instance of readline 42 | func newReadline(repl *repl.REPL) *readline { 43 | rl := &readline{ 44 | State: liner.NewLiner(), 45 | repl: repl, 46 | } 47 | home := homeDirectory() 48 | if home != "" { 49 | rl.historyFile = filepath.Join(home, HistoryFileName) 50 | } 51 | rl.SetTabCompletionStyle(liner.TabPrints) 52 | rl.SetWordCompleter(rl.Completer) 53 | return rl 54 | } 55 | 56 | // readHistory reads the history into the term 57 | func (rl *readline) ReadHistory() error { 58 | f, err := os.Open(rl.historyFile) 59 | if err != nil { 60 | return err 61 | } 62 | defer f.Close() 63 | _, err = rl.State.ReadHistory(f) 64 | if err != nil { 65 | return err 66 | } 67 | return nil 68 | } 69 | 70 | // writeHistory writes the history from the term 71 | func (rl *readline) WriteHistory() error { 72 | f, err := os.OpenFile(rl.historyFile, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666) 73 | if err != nil { 74 | return err 75 | } 76 | defer f.Close() 77 | _, err = rl.State.WriteHistory(f) 78 | if err != nil { 79 | return err 80 | } 81 | return nil 82 | } 83 | 84 | // Close the readline and write history 85 | func (rl *readline) Close() error { 86 | err := rl.State.Close() 87 | if err != nil { 88 | return err 89 | } 90 | if rl.historyFile != "" { 91 | err := rl.WriteHistory() 92 | if err != nil { 93 | return err 94 | } 95 | } 96 | return nil 97 | } 98 | 99 | // Completer takes the currently edited line with the cursor 100 | // position and returns the completion candidates for the partial word 101 | // to be completed. If the line is "Hello, wo!!!" and the cursor is 102 | // before the first '!', ("Hello, wo!!!", 9) is passed to the 103 | // completer which may returns ("Hello, ", {"world", "Word"}, "!!!") 104 | // to have "Hello, world!!!". 105 | func (rl *readline) Completer(line string, pos int) (head string, completions []string, tail string) { 106 | return rl.repl.Completer(line, pos) 107 | } 108 | 109 | // SetPrompt sets the current terminal prompt 110 | func (rl *readline) SetPrompt(prompt string) { 111 | rl.prompt = prompt 112 | } 113 | 114 | // Print prints the output 115 | func (rl *readline) Print(out string) { 116 | _, _ = os.Stdout.WriteString(out + "\n") 117 | } 118 | 119 | // RunREPL starts the REPL loop 120 | func RunREPL(replCtx *repl.REPL) error { 121 | if replCtx == nil { 122 | replCtx = repl.New(nil) 123 | } 124 | rl := newReadline(replCtx) 125 | replCtx.SetUI(rl) 126 | defer rl.Close() 127 | err := rl.ReadHistory() 128 | if err != nil { 129 | if !os.IsNotExist(err) { 130 | fmt.Printf("Failed to open history: %v\n", err) 131 | } 132 | } 133 | 134 | for { 135 | line, err := rl.Prompt(rl.prompt) 136 | if err != nil { 137 | if err == io.EOF { 138 | fmt.Printf("\n") 139 | break 140 | } 141 | fmt.Printf("Problem reading line: %v\n", err) 142 | continue 143 | } 144 | if line != "" { 145 | rl.AppendHistory(line) 146 | } 147 | err = rl.repl.Run(line) 148 | if err != nil { 149 | return err 150 | } 151 | } 152 | return nil 153 | } 154 | -------------------------------------------------------------------------------- /parser/stringescape.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package parser 6 | 7 | import ( 8 | "bytes" 9 | "strconv" 10 | 11 | "github.com/go-python/gpython/py" 12 | ) 13 | 14 | // DecodeEscape unescapes a backslash-escaped buffer 15 | // 16 | // byteMode indicates whether we are creating a unicode string or a bytes output 17 | func DecodeEscape(in *bytes.Buffer, byteMode bool) (out *bytes.Buffer, err error) { 18 | // Early exit if no escape sequences 19 | // NB in.Bytes() is cheap 20 | inBytes := in.Bytes() 21 | if !bytes.ContainsRune(inBytes, '\\') { 22 | return in, nil 23 | } 24 | out = new(bytes.Buffer) 25 | runes := bytes.Runes(inBytes) 26 | decodeHex := func(what byte, i, size int) error { 27 | i++ 28 | if i+size <= len(runes) { 29 | cout, err := strconv.ParseInt(string(runes[i:i+size]), 16, 32) 30 | if err != nil { 31 | return py.ExceptionNewf(py.ValueError, "invalid \\%c escape at position %d", what, i-2) 32 | } 33 | if byteMode { 34 | out.WriteByte(byte(cout)) 35 | } else { 36 | out.WriteRune(rune(cout)) 37 | } 38 | } else { 39 | return py.ExceptionNewf(py.ValueError, "truncated \\%c escape at position %d", what, i-2) 40 | } 41 | return nil 42 | } 43 | ignoreEscape := false 44 | for i := 0; i < len(runes); i++ { 45 | c := runes[i] 46 | if c != '\\' { 47 | out.WriteRune(c) 48 | continue 49 | } 50 | i++ 51 | if i >= len(runes) { 52 | return nil, py.ExceptionNewf(py.ValueError, "Trailing \\ in string") 53 | } 54 | c = runes[i] 55 | switch c { 56 | case '\n': 57 | case '\\': 58 | out.WriteRune('\\') 59 | case '\'': 60 | out.WriteRune('\'') 61 | case '"': 62 | out.WriteRune('"') 63 | case 'b': 64 | out.WriteRune('\b') 65 | case 'f': 66 | out.WriteRune('\014') // FF 67 | case 't': 68 | out.WriteRune('\t') 69 | case 'n': 70 | out.WriteRune('\n') 71 | case 'r': 72 | out.WriteRune('\r') 73 | case 'v': 74 | out.WriteRune('\013') // VT 75 | case 'a': 76 | out.WriteRune('\007') // BEL, not classic C 77 | case '0', '1', '2', '3', '4', '5', '6', '7': 78 | // 1 to 3 characters of octal escape 79 | cout := c - '0' 80 | if i+1 < len(runes) && '0' <= runes[i+1] && runes[i+1] <= '7' { 81 | i++ 82 | cout = (cout << 3) + runes[i] - '0' 83 | if i+1 < len(runes) && '0' <= runes[i+1] && runes[i+1] <= '7' { 84 | i++ 85 | cout = (cout << 3) + runes[i] - '0' 86 | } 87 | } 88 | if byteMode { 89 | out.WriteByte(byte(cout)) 90 | } else { 91 | out.WriteRune(cout) 92 | } 93 | case 'x': 94 | // \xhh exactly 2 characters of hex 95 | err = decodeHex('x', i, 2) 96 | if err != nil { 97 | return nil, err 98 | } 99 | i += 2 100 | // FIXME In a bytes literal, hexadecimal and 101 | // octal escapes denote the byte with the 102 | // given value. In a string literal, these 103 | // escapes denote a Unicode character with the 104 | // given value. 105 | case 'u': 106 | // \uxxxx Character with 16-bit hex value xxxx - 4 characters required 107 | if byteMode { 108 | ignoreEscape = true 109 | break 110 | } 111 | err = decodeHex('u', i, 4) 112 | if err != nil { 113 | return nil, err 114 | } 115 | i += 4 116 | case 'U': 117 | // \Uxxxxxxxx Character with 32-bit hex value xxxxxxxx - 8 characters required 118 | if byteMode { 119 | ignoreEscape = true 120 | break 121 | } 122 | 123 | err = decodeHex('U', i, 8) 124 | if err != nil { 125 | return nil, err 126 | } 127 | i += 8 128 | case 'N': 129 | // \N{name} Character named name in the Unicode database 130 | if byteMode { 131 | ignoreEscape = true 132 | break 133 | } 134 | // FIXME go can't do this as builtin so ignore for the moment 135 | ignoreEscape = true 136 | default: 137 | ignoreEscape = true 138 | } 139 | // ignore unrecognised escape 140 | if ignoreEscape { 141 | i-- 142 | out.WriteRune('\\') 143 | ignoreEscape = false 144 | } 145 | } 146 | return out, nil 147 | } 148 | -------------------------------------------------------------------------------- /py/tests/list.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | from libtest import assertRaises 6 | 7 | doc="str" 8 | assert str([]) == "[]" 9 | assert str([1,2,3]) == "[1, 2, 3]" 10 | assert str([1,[2,3],4]) == "[1, [2, 3], 4]" 11 | assert str(["1",[2.5,17,[]]]) == "['1', [2.5, 17, []]]" 12 | assert str([1, 1.0]) == "[1, 1.0]" 13 | 14 | doc="repr" 15 | assert repr([]) == "[]" 16 | assert repr([1,2,3]) == "[1, 2, 3]" 17 | assert repr([1,[2,3],4]) == "[1, [2, 3], 4]" 18 | assert repr(["1",[2.5,17,[]]]) == "['1', [2.5, 17, []]]" 19 | assert repr([1, 1.0]) == "[1, 1.0]" 20 | 21 | doc="enumerate" 22 | a = [e for e in enumerate([3,4,5,6,7], 4)] 23 | idxs = [4, 5, 6, 7, 8] 24 | values = [3, 4, 5, 6, 7] 25 | for idx, value in enumerate(values): 26 | assert idxs[idx] == a[idx][0] 27 | assert values[idx] == a[idx][1] 28 | 29 | doc="append" 30 | a = [1,2,3] 31 | a.append(4) 32 | assert repr(a) == "[1, 2, 3, 4]" 33 | a = ['a', 'b', 'c'] 34 | a.extend(['d', 'e', 'f']) 35 | assert repr(a) == "['a', 'b', 'c', 'd', 'e', 'f']" 36 | assertRaises(TypeError, lambda: [].append()) 37 | 38 | doc="mul" 39 | a = [1, 2, 3] 40 | assert a * 2 == [1, 2, 3, 1, 2, 3] 41 | assert a * 0 == [] 42 | assert a * -1 == [] 43 | 44 | doc="sort" 45 | # [].sort 46 | a = [3, 1.1, 1, 2] 47 | s1 = list(a) 48 | s1.sort() 49 | assert s1 == [1, 1.1, 2, 3] 50 | s1.sort() # sort a sorted list 51 | assert s1 == [1, 1.1, 2, 3] 52 | s2 = list(a) 53 | s2.sort(reverse=True) 54 | assert s2 == [3, 2, 1.1, 1] 55 | s2.sort() # sort a reversed list 56 | assert s2 == [1, 1.1, 2, 3] 57 | s3 = list(a) 58 | s3.sort(key=lambda l: l+1) # test lambda key 59 | assert s3 == [1, 1.1, 2, 3] 60 | s4 = [2.0, 2, 1, 1.0] 61 | s4.sort(key=lambda l: 0) # test stability 62 | assert s4 == [2.0, 2, 1, 1.0] 63 | assert [type(t) for t in s4] == [float, int, int, float] 64 | s4 = [2.0, 2, 1, 1.0] 65 | s4.sort() # test stability 66 | assert s4 == [1, 1.0, 2.0, 2] 67 | assert [type(t) for t in s4] == [int, float, float, int] 68 | s5 = [2.0, "abc"] 69 | assertRaises(TypeError, lambda: s5.sort()) 70 | s5 = [] 71 | s5.sort() 72 | assert s5 == [] 73 | s5 = [0] 74 | s5.sort() 75 | assert s5 == [0] 76 | s5 = [0, 1] 77 | # Sorting a list of len >= 2 with uncallable key must fail on all Python implementations. 78 | assertRaises(TypeError, lambda: s5.sort(key=1)) 79 | 80 | # list.sort([]) 81 | a = [3, 1.1, 1, 2] 82 | s1 = list(a) 83 | assert list.sort(s1) is None 84 | assert s1 == [1, 1.1, 2, 3] 85 | assert list.sort(s1) is None # sort a sorted list 86 | assert s1 == [1, 1.1, 2, 3] 87 | s2 = list(a) 88 | list.sort(s2, reverse=True) 89 | assert s2 == [3, 2, 1.1, 1] 90 | list.sort(s2) # sort a reversed list 91 | assert s2 == [1, 1.1, 2, 3] 92 | s3 = list(a) 93 | list.sort(s3, key=lambda l: l+1) # test lambda key 94 | assert s3 == [1, 1.1, 2, 3] 95 | s4 = [2.0, 2, 1, 1.0] 96 | list.sort(s4, key=lambda l: 0) # test stability 97 | assert s4 == [2.0, 2, 1, 1.0] 98 | assert [type(t) for t in s4] == [float, int, int, float] 99 | s4 = [2.0, 2, 1, 1.0] 100 | list.sort(s4) # test stability 101 | assert s4 == [1, 1.0, 2.0, 2] 102 | assert [type(t) for t in s4] == [int, float, float, int] 103 | s5 = [2.0, "abc"] 104 | assertRaises(TypeError, lambda: list.sort(s5)) 105 | s5 = [] 106 | list.sort(s5) 107 | assert s5 == [] 108 | s5 = [0] 109 | list.sort(s5) 110 | assert s5 == [0] 111 | s5 = [0, 1] 112 | # Sorting a list of len >= 2 with uncallable key must fail on all Python implementations. 113 | assertRaises(TypeError, lambda: list.sort(s5, key=1)) 114 | assertRaises(TypeError, lambda: list.sort(1)) 115 | 116 | class Index: 117 | def __index__(self): 118 | return 1 119 | 120 | a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 121 | b = Index() 122 | assert a[b] == 1 123 | assert a[b:10] == a[1:10] 124 | assert a[10:b:-1] == a[10:1:-1] 125 | 126 | class NonIntegerIndex: 127 | def __index__(self): 128 | return 1.1 129 | 130 | a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 131 | b = NonIntegerIndex() 132 | try: 133 | a[b] 134 | except TypeError: 135 | pass 136 | else: 137 | assert False, "TypeError not raised" 138 | 139 | try: 140 | a[b:10] 141 | except TypeError: 142 | pass 143 | else: 144 | assert False, "TypeError not raised" 145 | 146 | doc="finished" 147 | -------------------------------------------------------------------------------- /ast/walk_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ast 6 | 7 | import ( 8 | "fmt" 9 | "testing" 10 | ) 11 | 12 | func TestWalk(t *testing.T) { 13 | out := []string{} 14 | accumulate := func(ast Ast) bool { 15 | out = append(out, fmt.Sprintf("%T", ast)) 16 | return true 17 | } 18 | 19 | for _, test := range []struct { 20 | in Ast 21 | out []string 22 | }{ 23 | {nil, []string{}}, 24 | 25 | // An empty one of everything 26 | {&Module{}, []string{"*ast.Module"}}, 27 | {&Interactive{}, []string{"*ast.Interactive"}}, 28 | {&Expression{}, []string{"*ast.Expression"}}, 29 | {&Suite{}, []string{"*ast.Suite"}}, 30 | {&FunctionDef{}, []string{"*ast.FunctionDef"}}, 31 | {&ClassDef{}, []string{"*ast.ClassDef"}}, 32 | {&Return{}, []string{"*ast.Return"}}, 33 | {&Delete{}, []string{"*ast.Delete"}}, 34 | {&Assign{}, []string{"*ast.Assign"}}, 35 | {&AugAssign{}, []string{"*ast.AugAssign"}}, 36 | {&For{}, []string{"*ast.For"}}, 37 | {&While{}, []string{"*ast.While"}}, 38 | {&If{}, []string{"*ast.If"}}, 39 | {&With{}, []string{"*ast.With"}}, 40 | {&Raise{}, []string{"*ast.Raise"}}, 41 | {&Try{}, []string{"*ast.Try"}}, 42 | {&Assert{}, []string{"*ast.Assert"}}, 43 | {&Import{}, []string{"*ast.Import"}}, 44 | {&ImportFrom{}, []string{"*ast.ImportFrom"}}, 45 | {&Global{}, []string{"*ast.Global"}}, 46 | {&Nonlocal{}, []string{"*ast.Nonlocal"}}, 47 | {&ExprStmt{}, []string{"*ast.ExprStmt"}}, 48 | {&Pass{}, []string{"*ast.Pass"}}, 49 | {&Break{}, []string{"*ast.Break"}}, 50 | {&Continue{}, []string{"*ast.Continue"}}, 51 | {&BoolOp{}, []string{"*ast.BoolOp"}}, 52 | {&BinOp{}, []string{"*ast.BinOp"}}, 53 | {&UnaryOp{}, []string{"*ast.UnaryOp"}}, 54 | {&Lambda{}, []string{"*ast.Lambda"}}, 55 | {&IfExp{}, []string{"*ast.IfExp"}}, 56 | {&Dict{}, []string{"*ast.Dict"}}, 57 | {&Set{}, []string{"*ast.Set"}}, 58 | {&ListComp{}, []string{"*ast.ListComp"}}, 59 | {&SetComp{}, []string{"*ast.SetComp"}}, 60 | {&DictComp{}, []string{"*ast.DictComp"}}, 61 | {&GeneratorExp{}, []string{"*ast.GeneratorExp"}}, 62 | {&Yield{}, []string{"*ast.Yield"}}, 63 | {&YieldFrom{}, []string{"*ast.YieldFrom"}}, 64 | {&Compare{}, []string{"*ast.Compare"}}, 65 | {&Call{}, []string{"*ast.Call"}}, 66 | {&Num{}, []string{"*ast.Num"}}, 67 | {&Str{}, []string{"*ast.Str"}}, 68 | {&Bytes{}, []string{"*ast.Bytes"}}, 69 | {&NameConstant{}, []string{"*ast.NameConstant"}}, 70 | {&Ellipsis{}, []string{"*ast.Ellipsis"}}, 71 | {&Attribute{}, []string{"*ast.Attribute"}}, 72 | {&Subscript{}, []string{"*ast.Subscript"}}, 73 | {&Starred{}, []string{"*ast.Starred"}}, 74 | {&Name{}, []string{"*ast.Name"}}, 75 | {&List{}, []string{"*ast.List"}}, 76 | {&Tuple{}, []string{"*ast.Tuple"}}, 77 | {&Slice{}, []string{"*ast.Slice"}}, 78 | {&ExtSlice{}, []string{"*ast.ExtSlice"}}, 79 | {&Index{}, []string{"*ast.Index"}}, 80 | {&ExceptHandler{}, []string{"*ast.ExceptHandler"}}, 81 | {&Arguments{}, []string{"*ast.Arguments"}}, 82 | {&Arg{}, []string{"*ast.Arg"}}, 83 | {&Keyword{}, []string{"*ast.Keyword"}}, 84 | {&Alias{}, []string{"*ast.Alias"}}, 85 | {&WithItem{}, []string{"*ast.WithItem"}}, 86 | 87 | // Excercise the walk* closures 88 | {&Module{Body: []Stmt{&Pass{}}}, []string{"*ast.Module", "*ast.Pass"}}, 89 | {&Module{Body: []Stmt{&Pass{}, &Pass{}}}, []string{"*ast.Module", "*ast.Pass", "*ast.Pass"}}, 90 | {&Expression{Body: &Num{}}, []string{"*ast.Expression", "*ast.Num"}}, 91 | {&Attribute{Value: &Num{}}, []string{"*ast.Attribute", "*ast.Num"}}, 92 | {&List{Elts: []Expr{&Num{}, &Str{}}}, []string{"*ast.List", "*ast.Num", "*ast.Str"}}, 93 | {&ListComp{Elt: &Num{}, Generators: []Comprehension{{Target: &Num{}, Iter: &Str{}, Ifs: []Expr{&Num{}, &Str{}}}}}, []string{"*ast.ListComp", "*ast.Num", "*ast.Num", "*ast.Str", "*ast.Num", "*ast.Str"}}, 94 | } { 95 | out = nil 96 | Walk(test.in, accumulate) 97 | if len(out) != len(test.out) { 98 | t.Errorf("%q: differing length: want %#v got %#v", Dump(test.in), test.out, out) 99 | } 100 | for i := range out { 101 | if out[i] != test.out[i] { 102 | t.Errorf("%q: out[%d]: want %q got %q", Dump(test.in), i, test.out[i], out[i]) 103 | } 104 | } 105 | } 106 | } 107 | --------------------------------------------------------------------------------