1. 05 Oct, 2022 2 commits
    • Kirill Smelkov's avatar
      py.bench: Automatically discover benchmarks in test files · ffb40903
      Kirill Smelkov authored
      Since the beginning (9bf03d9c "py.bench: New command to benchmark python
      code similarly to `go test -bench`") py.bench was automatically
      discovering benchmarks in bench_*.py files only. This was inherited from
      wendelin.core which keeps its benchmarks in those files.
      
      However in pygolang, following Go convention(*), we already have several
      benchmarks that reside together with tests in same *_test.py files. And
      currently just running py.bench does not discover them.
      
      -> Let's fix this and teach py.bench to automatically discover
      benchmarks in the test files by default as well.
      
      Pytest's default is to look for tests in test_*.py and *_test.py (+).
      Add those patterns and also keep bench_*.py for backward compatibility.
      
      Before this patch running py.bench inside pygolang repository does not
      run any benchmark at all. After the patch py.bench runs all the
      benchmarks by default:
      
          (z-dev) kirr@deca:~/src/tools/go/pygolang$ py.bench
          ========================= test session starts ==========================
          platform linux2 -- Python 2.7.18, pytest-4.6.11, py-1.10.0, pluggy-0.13.1
          rootdir: /home/kirr/src/tools/go/pygolang
          plugins: timeout-1.4.2, profiling-1.7.0, mock-2.0.0
          collected 18 items
      
          pymod: golang/golang_str_test.py
          Benchmarkstddecode              2000000 0.756 µs/op
          Benchmarkudecode                20000   74.359 µs/op
          Benchmarkstdencode              3000000 0.327 µs/op
          Benchmarkbencode                40000   32.613 µs/op
      
          pymod: golang/golang_test.py
          Benchmarkpyx_select_nogil       500000  2.051 µs/op
          Benchmarkpyx_go_nogil           90000   12.177 µs/op
          Benchmarkpyx_chan_nogil         600000  1.826 µs/op
          Benchmarkgo                     80000   13.267 µs/op
          Benchmarkchan                   500000  2.076 µs/op
          Benchmarkselect                 300000  3.835 µs/op
          Benchmarkdef                    30000000        0.035 µs/op
          Benchmarkfunc_def               40000   29.387 µs/op
          Benchmarkcall                   30000000        0.043 µs/op
          Benchmarkfunc_call              2000000 0.819 µs/op
          Benchmarktry_finally            20000000        0.096 µs/op
          Benchmarkdefer                  600000  1.755 µs/op
      
          pymod: golang/sync_test.py
          Benchmarkworkgroup_empty        40000   25.807 µs/op
          Benchmarkworkgroup_raise        40000   31.637 µs/op                     [100%]
      
          =========================== warnings summary ===========================
      
      (*) see https://pkg.go.dev/cmd/go#hdr-Test_packages
      (+) see https://docs.pytest.org/en/7.1.x/reference/reference.html#confval-python_files
      
      /reviewed-by @jerome
      /reviewed-on !20
      ffb40903
    • Kirill Smelkov's avatar
      golang_str: Speedup utf-8 decoding a bit on py2 · 9cb7b210
      Kirill Smelkov authored
      We recently moved our custom UTF-8 encoding/decoding routines to Cython.
      Now we can start taking speedup advantage on C level to make our own
      UTF-8 decoder a bit less horribly slow on py2:
      
          name       old time/op  new time/op  delta
          stddecode   752ns ± 0%   743ns ± 0%   -1.19%  (p=0.000 n=9+10)
          udecode     216µs ± 0%    75µs ± 0%  -65.19%  (p=0.000 n=9+10)
          stdencode   328ns ± 2%   327ns ± 1%     ~     (p=0.252 n=10+9)
          bencode    34.1µs ± 1%  32.1µs ± 1%   -5.92%  (p=0.000 n=10+10)
      
      So it is ~ 3x speedup for u(), but still significantly slower compared
      to std unicode.decode('utf-8').
      
      Only low-hanging fruit here to make _utf_decode_rune a bit more prompt,
      since it sits in the most inner loop. In the future
      _utf8_decode_surrogateescape might be reworked as well to avoid
      constructing resulting unicode via py-level list of py-unicode character
      objects. And similarly for _utf8_encode_surrogateescape.
      
      On py3 the performance of std and u/b decode/encode is approximately the same.
      
      /trusted-by @jerome
      /reviewed-on nexedi/pygolang!19
      9cb7b210
  2. 04 Oct, 2022 4 commits
    • Kirill Smelkov's avatar
      golang_str,strconv: Fix decoding of rune-error · 598eb479
      Kirill Smelkov authored
      Error rune (u+fffd) is returned by _utf8_decode_rune to indicate an
      error in decoding. But the error rune itself is valid unicode codepoint:
      
         >>> x = u"�"
         >>> x
         u'\ufffd'
         >>> x.encode('utf-8')
         '\xef\xbf\xbd'
      
      This way only (r=_rune_error, size=1) should be treated by the caller as
      utf8 decoding error.
      
      But e.g. strconv.quote was not careful to also inspect the size, and this way
      was quoting � into just "\xef" instead of "\xef\xbf\xbd".
      _utf8_decode_surrogateescape was also subject to similar error.
      
      -> Fix it.
      
      Without the fix e.g. added test for strconv.quote fails as
      
          >           assert quote(tin) == tquoted
          E           assert '"\xef"' == '"�"'
          E             - "\xef"
          E             + "�"
      
      /reviewed-by @jerome
      /reviewed-at nexedi/pygolang!18
      598eb479
    • Kirill Smelkov's avatar
      golang_str: Move py3/py2 conditioning into _utf8_{encode,decode}_surrogateescape · ea5abe71
      Kirill Smelkov authored
      So that those routines could be just called and do what is expected
      without the caller caring whether it is py2 or py3. We will soon need to
      use those routines from several callsites, and having that py2/py3
      conditioning being spread over all usage places would be inconvenient.
      
      /reviewed-by @jerome
      /reviewed-at nexedi/pygolang!18
      ea5abe71
    • Kirill Smelkov's avatar
      strconv: Move functionality related to UTF8 encode/decode into _golang_str · 50b8cb7e
      Kirill Smelkov authored
      - Move _utf8_decode_rune, _utf8_decode_surrogateescape, _utf8_encode_surrogateescape out from strconv into _golang_str
      - Factor _bstr/_ustr code into pyb/pyu. _bstr/_ustr become plain wrappers over pyb/pyu.
      - work-around emerged golang  strconv dependency with at-runtime import.
      
      Moved routines belong to the main part of golang strings processing
      -> their home should be in _golang_str.pyx
      
      /reviewed-by @jerome
      /reviewed-at !18
      50b8cb7e
    • Kirill Smelkov's avatar
      golang: Move strings-related code to _golang_str "submodule" · e72a459f
      Kirill Smelkov authored
      We are going to significantly extend py-strings related functionality soon
      - to the point where amount of strings related code will be
      approximately the same compared to the amount of all other
      python-related code inside golang module.
      
      -> First move everything related to py strings to dedicated
      _golang_str.pyx as a preparatory step.
      
      Keep that new file included from _golang.pyx instead of being real new
      module, because we want strings functionality to be provided by golang
      main namespace itself, and to ease internal code interdependencies.
      
      Plain code movement.
      
      /reviewed-by @jerome
      /reviewed-at nexedi/pygolang!18
      e72a459f
  3. 26 Jan, 2022 15 commits
    • Kirill Smelkov's avatar
      pygolang v0.1 · 7b72d418
      Kirill Smelkov authored
      7b72d418
    • Kirill Smelkov's avatar
      golang: Fix print(_pystr) · 08dc5d10
      Kirill Smelkov authored
      On Python2 without .tp_print printing _pystr crashes as:
      
          pygolang$ ./golang/testprog/golang_test_str.py
          Traceback (most recent call last):
            File "./golang/testprog/golang_test_str.py", line 39, in <module>
              main()
            File "./golang/testprog/golang_test_str.py", line 34, in main
              print("print(qq(b)):", qq(sb))
          RuntimeError: print recursion
      
      See added comments for details.
      08dc5d10
    • Kirill Smelkov's avatar
      os += ReadFile · 2a35ef5b
      Kirill Smelkov authored
      Add convenient utility to read whole file and return its content
      similarly to Go. The code is taken from wendelin.core:
      
      https://lab.nexedi.com/nexedi/wendelin.core/blob/wendelin.core-2.0.alpha1-18-g38dde766/wcfs/client/wcfs_misc.cpp#L246-281
      2a35ef5b
    • Kirill Smelkov's avatar
      Nogil signals · e18adbab
      Kirill Smelkov authored
      Provide os/signal package that can be used to setup signal delivery to nogil
      channels. This way for user code signal handling becomes regular handling of a
      signalling channel instead of being something special or limited to only-main
      python thread. The rationale for why we need it is explained below:
      
      There are several problems with regular python's stdlib signal module:
      
      1. Python2 does not call signal handler from under blocked lock.acquire.
         This means that if the main thread is blocked waiting on a semaphore,
         signal delivery will be delayed indefinitely, similarly to e.g. problem
         described in nexedi/nxdtest!14 (comment 147527)
         where raising KeyboardInterrupt is delayed after SIGINT for many,
         potentially unbounded, seconds until ~semaphore wait finishes.
      
         Note that Python3 does not have this problem wrt stdlib locks and
         semaphores, but read below for the next point.
      
      2. all pygolang communication operations (channels send/recv, sync.Mutex,
         sync.RWMutex, sync.Sema, sync.WaitGroup, sync.WorkGroup, ...) run with
         GIL released, but if blocked do not handle EINTR and do not schedule
         python signal handler to run (on main thread).
      
         Even if we could theoretically adjust this behaviour of pygolang at python
         level to match Python3, there are also C++ and pyx/nogil worlds. And we want gil
         and nogil worlds to interoperate (see https://pypi.org/project/pygolang/#cython-nogil-api),
         so that e.g. if completely nogil code happens to run on the main thread,
         signal handling is still possible, even if that signal handling was setup at
         python level.
      
      With signals delivered to nogil channels both nogil world and python
      world can setup signal handlers and to be notified of them irregardles
      of whether main python thread is currently blocked in nogil wait or not.
      
      /reviewed-on nexedi/pygolang!17
      e18adbab
    • Kirill Smelkov's avatar
      golang: Provide __pystr internally · ce507f4e
      Kirill Smelkov authored
      To convert an object to str of current python.
      It will be handy to use __pystr when implementing __str__ methods.
      
      /reviewed-on !17
      ce507f4e
    • Kirill Smelkov's avatar
      Nogil IO · 4690460b
      Kirill Smelkov authored
      Provide C++ package "os" with File, Pipe, etc similarly to what is
      provided on Go side. The package works through IO methods provided by
      runtimes.
      
      We need IO facility because os/signal package will need to use
      pipe in cooperative IO mode in its receiving-loop goroutine.
      
      os.h and os.cpp are based on drafts from wendelin.core:
      
      https://lab.nexedi.com/nexedi/wendelin.core/blob/wendelin.core-2.0.alpha1-18-g38dde766/wcfs/client/wcfs_misc.h
      https://lab.nexedi.com/nexedi/wendelin.core/blob/wendelin.core-2.0.alpha1-18-g38dde766/wcfs/client/wcfs_misc.cpp
      
      /reviewed-on !17
      4690460b
    • Kirill Smelkov's avatar
      libgolang/gevent: Put explicit try/catch boundary for tasks spawned via go · 07cae4e9
      Kirill Smelkov authored
      Else as https://github.com/python-greenlet/greenlet/pull/285
      demonstrates there can be segmentation faults and crashes due to
      exceptions from one greenlet propagating to C stack of another greenlet.
      
      No test here. I've tried to do it, but with gevent (contrary to plain
      greenlets), spawning new task only schedules corresponding greenlet to
      run in the end of current event loop cycle instead of switching to
      created greenlet immediately. With this delaying, it was hard for me to
      develop corresponding test in a reasonable time.
      
      Hopefully having the test I've done for greenlet itself + hereby
      protection is good enough.
      
      /reviewed-on !17
      07cae4e9
    • Kirill Smelkov's avatar
      internal/atomic: New package · d358fa75
      Kirill Smelkov authored
      This package provides special kind of atomic that is automatically reset
      to zero after fork in child. This kind of atomic will be used in os
      package to implement IO that does not deadlock in Close after fork.
      
      /reviewed-on !17
      d358fa75
    • Kirill Smelkov's avatar
      internal/syscall: New package · c2471014
      Kirill Smelkov authored
      This package provides wrappers to OS system calls.
      Minimal functionality that will be used to implement os and os/signal pacakges.
      
      /reviewed-on !17
      c2471014
    • Kirill Smelkov's avatar
      libgolang: Export _runtime internally · 3a131a51
      Kirill Smelkov authored
      Package os will need to access runtime operations to implement IO.
      
      /reviewed-on !17
      3a131a51
    • Kirill Smelkov's avatar
      libgolang/{thread,gevent}: Switch runtimes to C++ · 3a838d24
      Kirill Smelkov authored
      We will soon need to use common functionality(*) from both runtimes and
      other packages. The "other packages" are all C++ and it is handy to keep
      common functionality in C++ as well. While we could also maintain
      `extern "C"` interface, it duplicates the work. Let's switch everything
      to C++ to ease further maintenance.
      
      (*) e.g. package internal/syscall from both runtimes and from package os.
      
      /reviewed-on !17
      3a838d24
    • Kirill Smelkov's avatar
      libgolang: Provide std::hash for chan · 1fad944d
      Kirill Smelkov authored
      Without defined std::hash it is not possible to use channels as keys in
      dict or set. We will be using set<chan> in os/signal package
      implementation.
      
      /reviewed-on !17
      1fad944d
    • Kirill Smelkov's avatar
      pyx.build: Add runtime/_libgolang.pxd to dependencies · 60de9538
      Kirill Smelkov authored
      I forgot to include it in ad00be70 (libgolang: Introduce runtimes).
      
      /reviewed-on !17
      60de9538
    • Kirill Smelkov's avatar
      pyx.build: Simplify listing dependencies · ff9beb02
      Kirill Smelkov authored
      /reviewed-on !17
      ff9beb02
    • Kirill Smelkov's avatar
      manifest += .nxdtest · ad84cf76
      Kirill Smelkov authored
          $ check-manifest
          lists of files in version control and sdist do not match!
          missing from sdist:
            .nxdtest
      
      /reviewed-on !17
      ad84cf76
  4. 08 Dec, 2021 5 commits
    • Kirill Smelkov's avatar
      pygolang v0.0.9 · e503beb0
      Kirill Smelkov authored
      e503beb0
    • Kirill Smelkov's avatar
      gpython: tests: Adjust test_pymain_syspath to current PyPy state · 57ab5f33
      Kirill Smelkov authored
      See https://foss.heptapod.net/pypy/pypy/-/issues/3610 for stdin case.
      -m started to behave as CPython.
      57ab5f33
    • Kirill Smelkov's avatar
      gpython: tests: test_pymain: Fix -i on PyPy3 · 6ca43f2d
      Kirill Smelkov authored
      On py3 'ps1' and b'ps1' are different keys and it was failing on PyPy3 as follows:
      
                  # -i after stdin (also tests interactive mode as -i forces interactive even on non-tty)
                  d = {
                      b'hellopy': b(hellopy),
                      b'ps1':     b'' # cpython emits prompt to stderr
                  }
                  if is_pypy and not is_gpython:
                      d['ps1'] = b'>>>> ' # native pypy emits prompt to stdout and >>>> instead of >>>
                  _ = pyout(['-i'], stdin=b'import hello\n', cwd=testdata)
          >       assert _ == b"%(ps1)shello\nworld\n['']\n%(ps1)s"           % d
          E       assert b">>>> hello\...\n['']\n>>>> " == b"hello\nworld\n['']\n"
          E         At index 0 diff: b'>' != b'h'
          E         Full diff:
          E         - b"hello\nworld\n['']\n"
          E         + b">>>> hello\nworld\n['']\n>>>> "
          E         ?   +++++                    +++++
      
          gpython/gpython_test.py:200: AssertionError
      
      Fix it.
      Amends e205dbf6 (gpython: Implement -i (interactive inspect after program run) + promised interactive-mode tests)
      6ca43f2d
    • Kirill Smelkov's avatar
      context: Fix deadlock when new context is created from already-canceled parent · 58d4cbfe
      Kirill Smelkov authored
      When _BaseCtx is setting up cancel propagation it locks a parent,
      checks for parent.err != nil, and, if it is, calls
      ctx._cancel(parent.err) _with_ _holding_ parent.mu. Since _cancel
      internally also goes through parents and locks them, this was deadlocking
      on the second call to parent.mu.lock().
      
      -> Fix it by calling ctx._cancel(err) in the constructor outside of
      parent lock.
      
      The bug was there from the beginning - from e9567c7b (context: New
      package that mirrors Go's context).
      
      /trusted-by @jerome
      /reviewed-on !16
      58d4cbfe
    • Kirill Smelkov's avatar
      context: Reorganize the code to make internal logic more clear · d0688e21
      Kirill Smelkov authored
      - _propagateCancel is used only in _BaseCtx constructor -> inline it
        there. Being run in the constructor makes it clear that this code
        works on new _BaseCtx object with empty set of children.
      
      - since _cancelFrom interacts with the code moved from _propagateCancel,
        also move it to be close to cancel propagation setup.
      
      No functional changes, just plain code movement.
      
      /trusted-by @jerome
      /reviewed-on !16
      d0688e21
  5. 16 Aug, 2021 1 commit
  6. 07 Apr, 2021 5 commits
    • Kirill Smelkov's avatar
    • Kirill Smelkov's avatar
      gpython: Run code in carefully prepared __main__ module · 2351dd27
      Kirill Smelkov authored
      Because else sys.modules['__main__'] points to the module of gpython
      itself, not user program. This difference can be important when e.g.
      user defines classes in main module and tries to pickle them. Such
      pickling will fail if gpython is not careful to run user's code in its
      own main.
      
      Without this patch added test fails as
      
            File "check_main.py", line 51, in <module>
              main()
            File "check_main.py", line 39, in main
              assert hasattr(mainmod, 'MyUniqueClassXYZ'),        dir(mainmod)
          AssertionError: ['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'main']
      
      The problem was discovered while trying to run test program from
      https://github.com/zopefoundation/ZEO/issues/166 :
      
          $ gpython zopenrace-zeo.py
          Traceback (most recent call last):
            File "/home/kirr/tmp/trashme/Z/py2.venv/bin/gpython", line 8, in <module>
              sys.exit(main())
            File "/home/kirr/tmp/trashme/Z/py2.venv/lib/python2.7/site-packages/gpython/__init__.py", line 395, in main
              pymain(argv, init)
            File "/home/kirr/tmp/trashme/Z/py2.venv/lib/python2.7/site-packages/gpython/__init__.py", line 266, in pymain
              run()
            File "/home/kirr/tmp/trashme/Z/py2.venv/lib/python2.7/site-packages/gpython/__init__.py", line 172, in run
              _execfile(filepath, g)
            File "/home/kirr/tmp/trashme/Z/py2.venv/lib/python2.7/site-packages/gpython/__init__.py", line 275, in _execfile
              six.exec_(code, globals, locals)
            File "/home/kirr/tmp/trashme/Z/py2.venv/lib/python2.7/site-packages/six.py", line 719, in exec_
              exec("""exec _code_ in _globs_, _locs_""")
            File "<string>", line 1, in <module>
            File "zopenrace-zeo.py", line 205, in <module>
              main()
            File "zopenrace-zeo.py", line 190, in main
              init()
            File "zopenrace-zeo.py", line 126, in init
              transaction.commit()
            File "/home/kirr/tmp/trashme/Z/py2.venv/lib/python2.7/site-packages/transaction/_manager.py", line 257, in commit
              return self.manager.commit()
            File "/home/kirr/tmp/trashme/Z/py2.venv/lib/python2.7/site-packages/transaction/_manager.py", line 134, in commit
              return self.get().commit()
            File "/home/kirr/tmp/trashme/Z/py2.venv/lib/python2.7/site-packages/transaction/_transaction.py", line 282, in commit
              reraise(t, v, tb)
            File "/home/kirr/tmp/trashme/Z/py2.venv/lib/python2.7/site-packages/transaction/_transaction.py", line 273, in commit
              self._commitResources()
            File "/home/kirr/tmp/trashme/Z/py2.venv/lib/python2.7/site-packages/transaction/_transaction.py", line 456, in _commitResources
              reraise(t, v, tb)
            File "/home/kirr/tmp/trashme/Z/py2.venv/lib/python2.7/site-packages/transaction/_transaction.py", line 430, in _commitResources
              rm.commit(self)
            File "/home/kirr/tmp/trashme/Z/py2.venv/lib/python2.7/site-packages/ZODB/Connection.py", line 497, in commit
              self._commit(transaction)
            File "/home/kirr/tmp/trashme/Z/py2.venv/lib/python2.7/site-packages/ZODB/Connection.py", line 546, in _commit
              self._store_objects(ObjectWriter(obj), transaction)
            File "/home/kirr/tmp/trashme/Z/py2.venv/lib/python2.7/site-packages/ZODB/Connection.py", line 578, in _store_objects
              p = writer.serialize(obj)  # This calls __getstate__ of obj
            File "/home/kirr/tmp/trashme/Z/py2.venv/lib/python2.7/site-packages/ZODB/serialize.py", line 430, in serialize
              return self._dump(meta, obj.__getstate__())
            File "/home/kirr/tmp/trashme/Z/py2.venv/lib/python2.7/site-packages/ZODB/serialize.py", line 439, in _dump
              self._p.dump(state)
          PicklingError: Can't pickle <class '__main__.PInt'>: attribute lookup __main__.PInt failed
      
      /reviewed-by @jerome
      /reviewed-on !15
      2351dd27
    • Kirill Smelkov's avatar
      gpython: Fix interactive mode to use stdout or stderr depending on what is a tty · 95c7cce9
      Kirill Smelkov authored
      To match python behaviour:
      
          $ python >/dev/null
          Python 2.7.18 (default, Apr 20 2020, 20:30:41)
          [GCC 9.3.0] on linux2
          Type "help", "copyright", "credits" or "license" for more information.
          >>> 1+1			# NOTE the prompt (printed to stderr)
      
          $ python 2>/dev/null
          >>> 1+1			# NOTE the prompt (printed to stdout)
      
      Tests pending.
      
      /reviewed-by @jerome
      /reviewed-on !15
      95c7cce9
    • Kirill Smelkov's avatar
      gpython: Handle program on stdin through just read+exec · 22fb559a
      Kirill Smelkov authored
      Not through interactive console, because it was printing prompts, while
      `python <prog.py` does not emit anything:
      
          $ cat prog.py
          print 'Hello World'
      
          $ python <prog.py
          Hello World
      
          $ gpython <prog.py
          Python 2.7.18 (default, Apr 20 2020, 20:30:41)
          [GCC 9.3.0] [GPython 0.0.8] [gevent 20.9.0] on linux2
          Type "help", "copyright", "credits" or "license" for more information.
          (InteractiveConsole)
          Hello World
      
      After the patch gpython output is the same as of python:
      
          $ gpython <prog.py
          Hello World
      
      Test coverage for interactive mode is pending.
      We'll add them soon in a follow-up patch after implementing -i.
      
      For _interact - by this patch logic - we should be dropping custom
      raw_input, since now _interact is called only when sys.stdin is tty.
      But we'll soon be invoking _interact when stdin is not a tty (for
      `gpython -i <prog.py`), so leave that logic in place as is.
      
      /reviewed-by @jerome
      /reviewed-on !15
      22fb559a
    • Kirill Smelkov's avatar
      gpython: Factor-out interactive-console functionality into its own function · 6cc4bf32
      Kirill Smelkov authored
      We will soon need this to be run from several places when implementing
      support for -i.
      
      /reviewed-by @jerome
      /reviewed-on !15
      6cc4bf32
  7. 26 Mar, 2021 1 commit
  8. 16 Mar, 2021 1 commit
  9. 14 Dec, 2020 2 commits
    • Kirill Smelkov's avatar
      *: Python3.9 switched __file__ to be always absolute · 4f28dddf
      Kirill Smelkov authored
      https://bugs.python.org/issue20443
      
      This broke test_defer_excchain_dump because
      testprog/golang_test_defer_excchain.txt is prepared with output where
      `python file` shows that file name in traceback as it was specified on
      the command line, e.g.
      
          .../pygolang/golang/testprog$ python golang_test_defer_excchain.py
          Traceback (most recent call last):
            File ".../pygolang/golang/__init__.py", line 103, in _
              return f(*argv, **kw)
            File "golang_test_defer_excchain.py", line 42, in main
              raise RuntimeError("err")
          RuntimeError: err
      
      while with py39 it became
      
          .../pygolang/golang/testprog$ python golang_test_defer_excchain.py
          Traceback (most recent call last):
            File ".../pygolang/golang/__init__.py", line 103, in _
              return f(*argv, **kw)
            File ".../pygolang/golang/testprog/golang_test_defer_excchain.py", line 42, in main
              raise RuntimeError("err")
          RuntimeError: err
      
      (notice the difference related to "line 42")
      
      -> Fix it:
      
      - amend the test to conditionally prefix golang_test_defer_excchain.py
        in expected output with PYGOLANG/golang/testprog/ if it is Python >= 3.9.
      - amend `gpython file` to match behaviour of underlying `python file`,
        so that the test passes unconditionally whether it is run by python or
        gpython.
      
      --------
      
      @jerome also says (nexedi/pygolang!13 (comment 122826)):
      
      FYI, buildout (and many zope packages) essentially use doctests for
      testing and they had to deal with similar differences in output.
      `zope.testing` comes with a doctest checker named "renormalizing" which
      normalize output with regular expressions, see for example setup of some
      buildout test [here](https://github.com/buildout/buildout/blob/db3d6e2fbf5d7ff2cc4b2507253c7a221cfc3e32/src/zc/buildout/tests.py#L3615-L3651).
      We definitely don't need this here for the moment, but maybe one day it
      can be useful.
      
      /reviewed-on nexedi/pygolang!13
      4f28dddf
    • Kirill Smelkov's avatar
      tox += CPython3.9 · 32167853
      Kirill Smelkov authored
      Debian testing recently switched default python3 to be python3.9.
      Let's make sure pygolang works with that python version.
      Currently some tests fail - this will be addressed in the next patch.
      
      /reviewed-on !13
      32167853
  10. 11 Dec, 2020 2 commits
    • Kirill Smelkov's avatar
      sync.WorkGroup: Provide "with" support · 6eb80104
      Kirill Smelkov authored
      So that it becomes possible to write
      
          with WorkGroup(ctx) as wg:
              wg.go(f1)
              wg.go(f2)
      
      instead of
      
          wg = WorkGroup(ctx)
          defer(wg.wait)
          wg.go(f1)
          wg.go(f2)
      
      or
      
          wg = WorkGroup(ctx)
          wg.go(f1)
          wg.go(f2)
          wg.wait()
      
      This is sometimes handy and is referred to as "structured concurrency"
      in Python world.
      
      sync.Sema, sync.Mutex, sync.RWMutex already support "with".
      sync.WaitGroup is imho too low-level, but we might consider adding
      "with" support for it in the future as well.
      
      In general pygolang way is to use defer instead of plugging all classes
      with __enter__/__exit__ "with" support, but for small well-known class of
      concurrency-related things its seems "with" support is worth it:
      
      - having "with" for sync.Mutex+co allows it to be used as a drop-in
        replacement instead of threading.Lock+co, and
      - having "with" for sync.WorkGroup - the most commonly-used tool to
        spawn jobs and wait for their completion - makes it on-par with
        "structured concurrency".
      
      /reviewed-on !12
      6eb80104
    • Kirill Smelkov's avatar
      sync.WorkGroup: Fix typo in wait · 85257b2a
      Kirill Smelkov authored
      WorkGroup methods work on PyWorkGroup, not on PyWaitGroup.
      This probably used to work because Cython ignores (?) provided type of self?
      
      /reviewed-on !12
      85257b2a
  11. 10 Dec, 2020 1 commit
    • Kirill Smelkov's avatar
      gpython: tests: Skip test_pymain_run_via_relpath on SlapOS · f15459b9
      Kirill Smelkov authored
      Similarly to test_pymain_syspath we cannot run this test on SlapOS because with
      buildout raw underlying python interpreter does not have access to eggs with
      which gpython script was generated.
      
      See 0fa9d6e7 and 92bb5bcc for details.
      
      Without this patch running pygolang tests fails on SlapOS as shown below:
      
          (pygolang-env) slapuser34@vifibcloud-rapidspace-hosting-007:~/srv/runner/software/44fe7dd3f13ecd100894c6368a35c055/parts/pygolang-dev$ gpython -m pytest -vsx -k relpath
          ================================================= test session starts ==================================================
          platform linux2 -- Python 2.7.18, pytest-4.6.11, py-1.9.0, pluggy-0.13.1 -- /srv/slapgrid/slappart34/srv/runner/software/44fe7dd3f13ecd100894c6368a35c055/bin/gpython
          cachedir: .pytest_cache
          rootdir: /srv/slapgrid/slappart34/srv/runner/software/44fe7dd3f13ecd100894c6368a35c055/parts/pygolang-dev
          collected 115 items / 114 deselected / 1 selected
      
          gpython/gpython_test.py::test_pymain_run_via_relpath Traceback (most recent call last):
            File "./__init__.py", line 511, in <module>
              main()
            File "./__init__.py", line 395, in main
              pymain(argv, init)
            File "./__init__.py", line 217, in pymain
              init()
            File "./__init__.py", line 364, in init
              import gevent
          ImportError: No module named gevent
          FAILED
      
          ======================================================= FAILURES =======================================================
          _____________________________________________ test_pymain_run_via_relpath ______________________________________________
      
              @gpython_only
              def test_pymain_run_via_relpath():
                  argv = ['-c',  'import sys; print(sys.version)']
                  out1 = pyout(                    argv, pyexe=sys.executable)
          >       out2 = pyout(['./__init__.py'] + argv, pyexe=sys._gpy_underlying_executable, cwd=here)
      
          gpython/gpython_test.py:301:
          _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
          golang/golang_test.py:1842: in pyout
              return pyrun(argv, stdin=stdin, stdout=stdout, stderr=stderr, **kw)
          _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
      
          argv = ['./__init__.py', '-c', 'import sys; print(sys.version)'], stdin = None, stdout = '', stderr = None
          kw = {'cwd': '/srv/slapgrid/slappart34/srv/runner/software/44fe7dd3f13ecd100894c6368a35c055/parts/pygolang-dev/gpython', 'pyexe': '/srv/slapgrid/slappart34/srv//runner//shared/python2.7/44578f5389723084b136574c34bc8023/bin/python2.7'}
          retcode = 1
      
              def pyrun(argv, stdin=None, stdout=None, stderr=None, **kw):
                  retcode, stdout, stderr = _pyrun(argv, stdin=stdin, stdout=stdout, stderr=stderr, **kw)
                  if retcode:
          >           raise RuntimeError(' '.join(argv) + '\n' + (stderr and str(stderr) or '(failed)'))
          E           RuntimeError: ./__init__.py -c import sys; print(sys.version)
          E           (failed)
      
          golang/golang_test.py:1836: RuntimeError
          ======================================= 1 failed, 114 deselected in 0.73 seconds =======================================
      
      Fixes 076cdd8f (gpython: Fix crash when invoked as e.g. ./bin/gpython).
      
      /reviewed-on !11
      f15459b9
  12. 06 Dec, 2020 1 commit