1. 27 Apr, 2023 19 commits
    • Kirill Smelkov's avatar
      golang: tests: Normalize paths to use / delimiter in doctests's got · 06935819
      Kirill Smelkov authored
      Else on Windows e.g. test_defer_excchain_traceback fails as
      
          E           Failed: not equal:
          E           Differences (unified diff with -expected +actual):
          E               @@ -1,8 +1,8 @@
          E                Traceback (most recent call last):
          E               -  File "PYGOLANG/golang/golang_test.py", line ..., in test_defer_excchain_traceback
          E               +  File "PYGOLANG\golang\golang_test.py", line 1524, in test_defer_excchain_traceback
          E                    alpha()
          E               -  File "PYGOLANG/golang/golang_test.py", line ..., in alpha
          E               +  File "PYGOLANG\golang\golang_test.py", line 1521, in alpha
          E                    beta()
          E               -  File "PYGOLANG/golang/golang_test.py", line ..., in beta
          E               +  File "PYGOLANG\golang\golang_test.py", line 1520, in beta
          E                    raise RuntimeError("gamma")
          E                RuntimeError: gamma
      06935819
    • Kirill Smelkov's avatar
      golang: Prepare path for libgolang.dll before importing _golang · a5ce8175
      Kirill Smelkov authored
      Else it fails to import on Windows:
      
          collecting ... 05c8:err:module:import_dll Library libgolang.dll (which is needed by L"Z:\\home\\kirr\\src\\tools\\go\\pygo-win\\pygolang\\golang\\_golang.cp310-win_amd64.pyd") not found
          collected 0 items / 1 error
      
          =========================================================== ERRORS ===========================================================
          ___________________________________________ ERROR collecting golang/golang_test.py ___________________________________
          ImportError while importing test module 'Z:\home\kirr\src\tools\go\pygo-win\pygolang\golang\golang_test.py'.
          Hint: make sure your test modules/packages have valid Python names.
          Traceback:
          C:\Program Files\Python310\lib\importlib\__init__.py:126: in import_module
              return _bootstrap._gcd_import(name[level:], package, level)
          golang\__init__.py:45: in <module>
              from golang._golang import _pysys_exc_clear as _sys_exc_clear
          E   ImportError: DLL load failed while importing _golang: Модуль не найден.
      
      We need to increase required setuptools_dso version because dylink_prepare_dso
      and generation of *_dsoinfo.py modules was done after setuptools_dso 2.
      a5ce8175
    • Kirill Smelkov's avatar
      .gitignore += *.lib *.exp · 0b97e0a8
      Kirill Smelkov authored
      On Windows this files are generated when linking alongside *.dll files
      if there are exported symbols.
      
      See also https://github.com/mdavidsaver/setuptools_dso/pull/25.
      0b97e0a8
    • Kirill Smelkov's avatar
      setup: We need to link libpyxruntime DSO to py runtime · fbed01b0
      Kirill Smelkov authored
      Libpyxruntime by definition contains python-specific code and we already
      compile it with include path having python includes in it. However on
      windows pyconfig.h contains
      
          #pragma comment(lib,"python3.lib")
      
      which instructs the linker to automatically link to that library. And
      without proper library path the link fails:
      
          Z:\home\kirr\src\tools\go\pygo-win\BuildTools\vc\tools\msvc\14.35.32215\bin\Hostx64\x64\link.exe /nologo /INCREMENTAL:NO /LTCG /DLL /MANIFEST:EMBED,ID=2 /MANIFESTUAC:NO /LIBPATH:build\lib.win-amd64-cpython-31
          0\golang\runtime /LIBPATH:z:\home\kirr\src\tools\go\pygo-win\BuildTools\vc\tools\msvc\14.35.32215\lib\x64 /LIBPATH:z:\home\kirr\src\tools\go\pygo-win\BuildTools\kits\10\lib\10.0.22000.0\ucrt\x64 /LIBPATH:z:\h
          ome\kirr\src\tools\go\pygo-win\BuildTools\kits\10\lib\10.0.22000.0\um\x64 libgolang.lib build\temp.win-amd64-cpython-310\Release\golang/runtime/libpyxruntime.obj /OUT:build\lib.win-amd64-cpython-310\golang\ru
          ntime\libpyxruntime.dll /IMPLIB:build\lib.win-amd64-cpython-310\golang\runtime\libpyxruntime.lib
          LINK : fatal error LNK1104: cannot open file 'python310.lib'
      
      -> Fix it by providing proper library path for python.dll .
      
      We need to do it ourselves only for libpyxruntime dso because for py
      extensions this is automatically done by distutils out of the box.
      fbed01b0
    • Kirill Smelkov's avatar
      libgolang/gevent: Adjust to support Windows · 437bbd56
      Kirill Smelkov authored
      On Windows rthere is no fcntl
      
          golang/runtime\_runtime_gevent.cpp(4811): error C3861: 'S_ISBLK': identifier not found
          golang/runtime\_runtime_gevent.cpp(4823): error C2039: 'Fcntl': is not a member of 'golang::internal::syscall'
          .\golang/runtime/internal/syscall.h(36): note: see declaration of 'golang::internal::syscall'
          golang/runtime\_runtime_gevent.cpp(4823): error C2065: 'F_GETFL': undeclared identifier
          golang/runtime\_runtime_gevent.cpp(4823): error C3861: 'Fcntl': identifier not found
          golang/runtime\_runtime_gevent.cpp(4870): error C2065: 'O_ACCMODE': undeclared identifier
          golang/runtime\_runtime_gevent.cpp(4889): error C2039: 'Fcntl': is not a member of 'golang::internal::syscall'
          .\golang/runtime/internal/syscall.h(36): note: see declaration of 'golang::internal::syscall'
          golang/runtime\_runtime_gevent.cpp(4889): error C2065: 'F_SETFL': undeclared identifier
          golang/runtime\_runtime_gevent.cpp(4889): error C2065: 'O_NONBLOCK': undeclared identifier
          golang/runtime\_runtime_gevent.cpp(4889): error C3861: 'Fcntl': identifier not found
      
      and even if there would be gevent does not provide cooperative version
      of FileObject for non-POSIX platforms.
      
      -> Always use FileObjectThread on windows even for pipes.
      437bbd56
    • Kirill Smelkov's avatar
      os/signal: Use windows-specific code to turn wakeup pipe into nonblocking mode · 45473979
      Kirill Smelkov authored
      There is no fcntl on windows. Named pipes can be set into nonblocking
      mode via SetNamedPipeHandleState(PIPE_NOWAIT).
      45473979
    • Kirill Smelkov's avatar
      os/signal: Internally emulate sigaction via signal · acc6c3f1
      Kirill Smelkov authored
      On MSVC there is no sigaction and we have to use signal. Fortunately
      our sigaction usage in os/signal can be practically emulated because we
      do not use all features of sigaction and because on MSVC there is
      SIG_GET signal extension to query current signal handler without
      resetting it.
      
      Because the emulation is limited we keep it in os/signal.cpp instead of
      adjusting syscall::Sigaction .
      acc6c3f1
    • Kirill Smelkov's avatar
      os/signal: Factor call to sys::Sigaction into sys_sigaction internal helper · b15fa818
      Kirill Smelkov authored
      On MSVC we will need to use signal(2), because there is no sigaction,
      and sigaction will be partly emulated through that. Move signal setting
      code into common place as a preparatory step.
      b15fa818
    • Kirill Smelkov's avatar
      internal/syscall: Initial adjust to support MSVC · d3bf3b7d
      Kirill Smelkov authored
      - there is no strerror_r, but strerror_s instead
      - fcntl is not supported
      - we need to explicitly tell open to open files in binary mode
      - pipes are created via _pipe and explicitly in binary mode
      - there is no sigaction.
      d3bf3b7d
    • Kirill Smelkov's avatar
      internal/syscall: Adjust Pipe to take flags · 63b5c1d9
      Kirill Smelkov authored
      This way e.g. O_CLOEXEC can be passed in at pipe creation time removing
      potential race in between pipe+fcntl vs exec in the middle. It will also
      help in windows porting in follow-up patches becuase there is no fcntl
      on Windows, but pipe with O_CLOEXEC semantic can be created directly
      with flags=_O_NOINHERIT.
      63b5c1d9
    • Kirill Smelkov's avatar
      internal/atomic: Adjust to support MSVC · 534033a5
      Kirill Smelkov authored
      There is no fork on windows and that we do not need to install atfork
      handler at all.
      
          golang/runtime/internal/atomic.cpp(23): fatal error C1083: Cannot open include file: 'pthread.h': No such file or directory
      534033a5
    • Kirill Smelkov's avatar
      fmt: Adjust to support MSVC · 5098decb
      Kirill Smelkov authored
      - there is no __attribute__ on that compiler
      - sadly va_start is rejected to work on reference:
      
          z:\home\kirr\src\tools\go\pygo-win\BuildTools\vc\tools\msvc\14.35.32215\include\vadefs.h(194): error C2338: static_assert failed: 'va_start argument must not have reference type and must not be parenthesized'
          golang/fmt.cpp(53): note: see reference to class template instantiation '__vcrt_assert_va_start_is_not_reference<const golang::string &>' being compiled
      
        -> change format from string& to be string in sprintf.
      
        Probably it should be ok in practice from performance point of view because
        string contains only pointer to data, not the data itself.
      5098decb
    • Kirill Smelkov's avatar
      os: Adjust to support MSVC · 22a597a0
      Kirill Smelkov authored
      - There is no S_IRUSR & friends  -> use _S_IREAD & co.
      - Tthere is no sys_siglist       -> kind-of define it ourselves for all documented signals.
      22a597a0
    • Kirill Smelkov's avatar
      libgolang: Use custom list_entry on MSVC · 715fa60b
      Kirill Smelkov authored
      list_entry, as provided by linux/list.h, uses statement expressions(*) which
      are not supported by MSVC:
      
          #define list_entry(ptr, type, member) ({              \
                  const typeof( ((type *)0)->member ) *__mptr = (ptr);   \
                  (type *)( (char *)__mptr - offsetof(type,member) );})
      
          golang/runtime/libgolang.cpp(439): error C2059: syntax error: '{'
          golang/runtime/libgolang.cpp(439): error C2143: syntax error: missing ';' before '{'
          golang/runtime/libgolang.cpp(439): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
          golang/runtime/libgolang.cpp(439): error C2440: 'initializing': cannot convert from 'list_head' to 'int'
          golang/runtime/libgolang.cpp(439): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
          golang/runtime/libgolang.cpp(439): error C2143: syntax error: missing ';' before '*'
          golang/runtime/libgolang.cpp(439): error C2065: '__mptr': undeclared identifier
          golang/runtime/libgolang.cpp(439): error C2059: syntax error: ')'
      
      -> work it around by defining list_entry with C++ lambda.
      
      (*) https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
      715fa60b
    • Kirill Smelkov's avatar
      libgolang: random_shuffle -> shuffle · 1845b93d
      Kirill Smelkov authored
      There is no std::random_shuffle on `MSVC /std:c++20` because it was removed in C++17:
      
          golang/runtime/libgolang.cpp(946): error C2039: 'random_shuffle': is not a member of 'std'
          z:\home\kirr\src\tools\go\pygo-win\BuildTools\vc\tools\msvc\14.35.32215\include\random(34): note: see declaration of 'std'
          golang/runtime/libgolang.cpp(946): error C3861: 'random_shuffle': identifier not found
      
          https://en.cppreference.com/w/cpp/algorithm/random_shuffle
      1845b93d
    • Kirill Smelkov's avatar
      libgolang: Provide custom bzero, mode_t and unistd.h on MSVC · 35ed2492
      Kirill Smelkov authored
      We need unistd.h because e.g. `cimport posix.stat` forces inclusion of unistd.h
      even if we use part of posix.stat that is available everywhere.
      
      We use bzero in libgolang.cpp - provide it in similar way via compat
      strings.h because on windows there is only string.h (no "s" in the end).
      
      We also use mode_t in several places - provide it via typedef in
      libgolang.h directly, because fcntl.h is present on MSVC.
      35ed2492
    • Kirill Smelkov's avatar
      pyx.build: Adjust compile flags if we are on Windows/MSVC · 5f107215
      Kirill Smelkov authored
      MSVC does not accept -std=c++11 but instead accepts /std:c++11. We cannot
      use that however because with /std:c++11 compilation fails with
      
          .\golang/libgolang.h(280): error C7555: use of designated initializers requires at least '/std:c++20'
          .\golang/libgolang.h(294): error C7555: use of designated initializers requires at least '/std:c++20'
          .\golang/libgolang.h(308): error C7555: use of designated initializers requires at least '/std:c++20'
      
      for
      
          276 // _selsend creates `_chansend(ch, ptx)` case for _chanselect.
          277 static inline
          278 _selcase _selsend(_chan *ch, const void *ptx) {
          279     _selcase _ = {
          280         .ch     = ch,			<-- NOTE
          281         .op     = _CHANSEND,
          282         .flags  = (enum _selflags)0,
          283         .user   = 0xff,
          284     };
      
      and similar places.
      
      -> Use /std:c++20 instead as MSVC advices  (WOW designated initializers are C99
      and it took 21 years for C++ to catch up).
      
      We also need to adjust exception handling mode into "fully conformant" instead
      of "assume extern C cannot throw" setuptools default so that `panic` and other
      extern C libgolang functions generate exceptions that could be caught
      instead of crashing programs.
      
      For C vs C++ flags we also need to adjust our build_ext, because the way
      setuptools/distutils organize compilation for MSVC is different from
      UNIX compilers.
      5f107215
    • Kirill Smelkov's avatar
      setup: Reuse golang.pyx.build to build DSOs · b5bd136c
      Kirill Smelkov authored
      We already reuse golang.pyx.build in setup.py to build Extensions, but
      DSOs were built via setuptools_dso directly so far. To support Windows
      we will soon need to adjust compilation flags for both Extensions and
      DSOs. It makes sense to first concentrate build recipes in one place
      before doing that.
      
      -> Move/factor DSOs build flags into golang.pyx.build and reuse that
      from setup.
      b5bd136c
    • Kirill Smelkov's avatar
      *: Fix dll export annotation everywhere · 0ffef2d8
      Kirill Smelkov authored
      We already try to annotate functions exported from DSOs as such.
      However so far we were testing only on Linux and macOS where
      -fvisibility=hidden is not the default, and thus linker forgives us
      annotation mistakes. But on Windows the default is to have hidden
      visibility out of the box and so the linker becomes strict about this.
      
      -> Fix all issues caught there.
      
      For example here is how it breaks if ~_interface() is not annotated properly:
      
          building 'golang.runtime.libpyxruntime' DSO as build\lib.win-amd64-cpython-310\golang\runtime\libpyxruntime.dll
          ...
          Z:\home\kirr\src\tools\go\pygo-win\BuildTools\vc\tools\msvc\14.35.32215\bin\Hostx64\x64\link.exe /nologo /INCREMENTAL:NO /LTCG /DLL /MANIFEST:EMBED,ID=2 /MANIFESTUAC:NO "/LIBPATH:C:\Program Files\Python310\li
          bs" /LIBPATH:build\lib.win-amd64-cpython-310\golang\runtime /LIBPATH:z:\home\kirr\src\tools\go\pygo-win\BuildTools\vc\tools\msvc\14.35.32215\lib\x64 /LIBPATH:z:\home\kirr\src\tools\go\pygo-win\BuildTools\kits
          \10\lib\10.0.22000.0\ucrt\x64 /LIBPATH:z:\home\kirr\src\tools\go\pygo-win\BuildTools\kits\10\lib\10.0.22000.0\um\x64 libgolang.lib build\temp.win-amd64-cpython-310\Release\golang/runtime/libpyxruntime.obj /OU
          T:build\lib.win-amd64-cpython-310\golang\runtime\libpyxruntime.dll /IMPLIB:build\lib.win-amd64-cpython-310\golang\runtime\libpyxruntime.lib
             Creating library build\lib.win-amd64-cpython-310\golang\runtime\libpyxruntime.lib and object build\lib.win-amd64-cpython-310\golang\runtime\libpyxruntime.exp
      
          libpyxruntime.obj : error LNK2001: unresolved external symbol "protected: __cdecl golang::_interface::~_interface(void)" (??1_interface@golang@@IEAA@XZ)
          build\lib.win-amd64-cpython-310\golang\runtime\libpyxruntime.dll : fatal error LNK1120: 1 unresolved externals
          error: command 'Z:\\home\\kirr\\src\\tools\\go\\pygo-win\\BuildTools\\vc\\tools\\msvc\\14.35.32215\\bin\\Hostx64\\x64\\link.exe' failed with exit code 1120
      0ffef2d8
  2. 25 Apr, 2023 1 commit
    • Kirill Smelkov's avatar
      check-manifest · 3a83e8f6
      Kirill Smelkov authored
          (z-dev) kirr@deca:~/src/tools/go/pygolang$ check-manifest
          lists of files in version control and sdist do not match!
          missing from VCS:
            golang/pyx/runtime.cpp
            golang/runtime/libgolang_dsoinfo.py
            golang/runtime/libpyxruntime_dsoinfo.py
      
      - golang/pyx/runtime.cpp is generated from golang/pyx/runtime.pyx
      - *_dsoinfo.py files are generated by setuptools_dso during the build
      3a83e8f6
  3. 16 Apr, 2023 6 commits
    • Kirill Smelkov's avatar
      tox += CPython 3.10, 3.11 · 7fb630c1
      Kirill Smelkov authored
      Tests now pass with those versions.
      Remove py38-dbg from test matrix ad this version is now a bit outdated
      and having just py38 without dbg there seems enough.
      7fb630c1
    • Kirill Smelkov's avatar
      tox: Fix list of py in "debug => use -s" to match main list of py interpreters · 462d6beb
      Kirill Smelkov authored
      I forgot to add py38 and py39 here while adding them in 792cbd6c (tox:
      -py36,py37d  +py38d, py38) and 32167853 (tox += CPython3.9). The former
      commit also removed py37d from test matrix.
      462d6beb
    • Kirill Smelkov's avatar
      golang: tests: Fix traceback tests for Python ≥ 3.10 · 53cd02b3
      Kirill Smelkov authored
      Python changed traceback output a bit:
      
      - added '^^^^^^^' and '~~^~~'
      - changed lineno of frame corresponding to __exit__ to be at line of `with ...`
      
      -> Adjust to that by introducing a bit more generalized conditionals in
         assertDoc.
      
      Here is, for example, how test_defer_excchain_traceback was failing on py3.11:
      
      E           Failed: not equal:
      E           Differences (unified diff with -expected +actual):
      E               @@ -1,6 +1,7 @@
      E                Traceback (most recent call last):
      E               -  File "PYGOLANG/golang/__init__.py", line ..., in _
      E               +  File "PYGOLANG/golang/__init__.py", line 103, in _
      E                    return f(*argv, **kw)
      E               -  File "PYGOLANG/golang/golang_test.py", line ..., in caller
      E               +           ^^^^^^^^^^^^^^
      E               +  File "PYGOLANG/golang/golang_test.py", line 1550, in caller
      E                    raise RuntimeError("ccc")
      E                RuntimeError: ccc
      E               @@ -9,7 +10,7 @@
      E                <BLANKLINE>
      E                Traceback (most recent call last):
      E               -  File "PYGOLANG/golang/__init__.py", line ..., in __exit__
      E               +  File "PYGOLANG/golang/__init__.py", line 180, in __exit__
      E                    d()
      E               -  File "PYGOLANG/golang/golang_test.py", line ..., in q2
      E               +  File "PYGOLANG/golang/golang_test.py", line 1548, in q2
      E                    raise RuntimeError("bbb")
      E                RuntimeError: bbb
      E               @@ -18,14 +19,16 @@
      E                <BLANKLINE>
      E                Traceback (most recent call last):
      E               -  File "PYGOLANG/golang/golang_test.py", line ..., in test_defer_excchain_traceback
      E               +  File "PYGOLANG/golang/golang_test.py", line 1553, in test_defer_excchain_traceback
      E                    caller()
      E               -  ...
      E               -  File "PYGOLANG/golang/__init__.py", line ..., in _
      E               -    return f(*argv, **kw)
      E               -  File "PYGOLANG/golang/__init__.py", line ..., in __exit__
      E               +  File "/home/kirr/src/tools/go/py3.venv/lib/python3.11/site-packages/decorator.py", line 232, in fun
      E               +    return caller(func, *(extras + args), **kw)
      E               +           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      E               +  File "PYGOLANG/golang/__init__.py", line 102, in _
      E               +    with __goframe__:
      E               +  File "PYGOLANG/golang/__init__.py", line 179, in __exit__
      E               +    with __goframe__:
      E               +  File "PYGOLANG/golang/__init__.py", line 180, in __exit__
      E                    d()
      E               -  File "PYGOLANG/golang/__init__.py", line ..., in __exit__
      E               -    d()
      E               -  File "PYGOLANG/golang/golang_test.py", line ..., in q1
      E               +  File "PYGOLANG/golang/golang_test.py", line 1545, in q1
      E                    raise RuntimeError("aaa")
      E                RuntimeError: aaa
      53cd02b3
    • Kirill Smelkov's avatar
      golang: tests: Fix for IPython ≥ 8 · dfe75e9a
      Kirill Smelkov authored
      IPython8 changed format of output tracebacks (see
      https://github.com/ipython/ipython/commit/02660f35 and further). Adding
      test support for later versions would add maintenance cost on pygolang
      side, but testing this is actually not needed, since we activate
      IPython-related patch only on py2 and for py2 the latest ipython version
      is ipython5.
      
      -> So simply skip this test if we see we have IPython ≥ 8.
         Skip it only on py3 just in case.
      
      For the reference here is how diff in between running
      golang_test_defer_excchain.py on IPython7 and IPython8 looks:
      
          diff --git a/golang/testprog/7 b/golang/testprog/8
          index 5c31ed2..8fea57b 100644
          --- a/golang/testprog/7
          +++ b/golang/testprog/8
          @@ -1,101 +1,91 @@
          -[22;0t]0;IPython: golang/testprog---------------------------------------------------------------------------
          +---------------------------------------------------------------------------
           RuntimeError                              Traceback (most recent call last)
          -~/src/tools/go/pygolang-master/golang/__init__.py in _(f, *argv, **kw)
          -    102         with __goframe__:
          ---> 103             return f(*argv, **kw)
          -    104
          +File ~/src/tools/go/pygolang-master/golang/__init__.py:103, in _func.<locals>._(f, *argv, **kw)
          +    102 with __goframe__:
          +--> 103     return f(*argv, **kw)
      
          -~/src/tools/go/pygolang-master/golang/testprog/golang_test_defer_excchain.py in main()
          -     41     defer(d1)
          ----> 42     raise RuntimeError("err")
          -     43
          +File ~/src/tools/go/pygolang-master/golang/testprog/golang_test_defer_excchain.py:42, in main()
          +     41 defer(d1)
          +---> 42 raise RuntimeError("err")
      
           RuntimeError: err
      
           During handling of the above exception, another exception occurred:
      
           RuntimeError                              Traceback (most recent call last)
          -~/src/tools/go/pygolang-master/golang/__init__.py in __exit__(__goframe__, exc_type, exc_val, exc_tb)
          -    179             with __goframe__:
          ---> 180                 d()
          -    181
          +File ~/src/tools/go/pygolang-master/golang/__init__.py:180, in _GoFrame.__exit__(__goframe__, exc_type, exc_val, exc_tb)
          +    179     with __goframe__:
          +--> 180         d()
          +    182 return __goframe__.recovered
      
          -~/src/tools/go/pygolang-master/golang/testprog/golang_test_defer_excchain.py in d1()
          +File ~/src/tools/go/pygolang-master/golang/testprog/golang_test_defer_excchain.py:31, in d1()
                30 def d1():
           ---> 31     raise RuntimeError("d1: aaa")
          -     32 def d2():
      
           RuntimeError: d1: aaa
      
           During handling of the above exception, another exception occurred:
      
           ZeroDivisionError                         Traceback (most recent call last)
          -~/src/tools/go/pygolang-master/golang/__init__.py in __exit__(__goframe__, exc_type, exc_val, exc_tb)
          -    179             with __goframe__:
          ---> 180                 d()
          -    181
          +File ~/src/tools/go/pygolang-master/golang/__init__.py:180, in _GoFrame.__exit__(__goframe__, exc_type, exc_val, exc_tb)
          +    179     with __goframe__:
          +--> 180         d()
          +    182 return __goframe__.recovered
      
          -~/src/tools/go/pygolang-master/golang/testprog/golang_test_defer_excchain.py in d2()
          +File ~/src/tools/go/pygolang-master/golang/testprog/golang_test_defer_excchain.py:33, in d2()
                32 def d2():
           ---> 33     1/0
          -     34 def d3():
      
           ZeroDivisionError: division by zero
      
           During handling of the above exception, another exception occurred:
      
           RuntimeError                              Traceback (most recent call last)
          -/usr/lib/python3.11/runpy.py in run_module(mod_name, init_globals, run_name, alter_sys)
          +File <frozen runpy>:226, in run_module(mod_name, init_globals, run_name, alter_sys)
      
          -/usr/lib/python3.11/runpy.py in _run_module_code(code, init_globals, mod_name, mod_spec, pkg_name, script_name)
          +File <frozen runpy>:98, in _run_module_code(code, init_globals, mod_name, mod_spec, pkg_name, script_name)
      
          -/usr/lib/python3.11/runpy.py in _run_code(code, run_globals, init_globals, mod_name, mod_spec, pkg_name, script_name)
          +File <frozen runpy>:88, in _run_code(code, run_globals, init_globals, mod_name, mod_spec, pkg_name, script_name)
      
          -~/src/tools/go/pygolang-master/golang/testprog/golang_test_defer_excchain.py in <module>
          -     43
          +File ~/src/tools/go/pygolang-master/golang/testprog/golang_test_defer_excchain.py:45
          +     42     raise RuntimeError("err")
                44 if __name__ == "__main__":
           ---> 45     main()
      
          -~/src/tools/go/py3.venv/lib/python3.11/site-packages/decorator.py in fun(*args, **kw)
          -    230             if not kwsyntax:
          -    231                 args, kw = fix(args, kw, sig)
          ---> 232             return caller(func, *(extras + args), **kw)
          -    233     fun.__name__ = func.__name__
          -    234     fun.__doc__ = func.__doc__
          +File ~/src/tools/go/py3.venv/lib/python3.11/site-packages/decorator.py:232, in decorate.<locals>.fun(*args, **kw)
          +    230 if not kwsyntax:
          +    231     args, kw = fix(args, kw, sig)
          +--> 232 return caller(func, *(extras + args), **kw)
      
          -~/src/tools/go/pygolang-master/golang/__init__.py in _(f, *argv, **kw)
          -    100         # run f under separate frame, where defer will register calls.
          -    101         __goframe__ = _GoFrame()
          ---> 102         with __goframe__:
          -    103             return f(*argv, **kw)
          -    104
          +File ~/src/tools/go/pygolang-master/golang/__init__.py:102, in _func.<locals>._(f, *argv, **kw)
          +     99 def _(f, *argv, **kw):
          +    100     # run f under separate frame, where defer will register calls.
          +    101     __goframe__ = _GoFrame()
          +--> 102     with __goframe__:
          +    103         return f(*argv, **kw)
      
          -~/src/tools/go/pygolang-master/golang/__init__.py in __exit__(__goframe__, exc_type, exc_val, exc_tb)
          -    177
          -    178             # even if d panics - we have to call other defers
          ---> 179             with __goframe__:
          -    180                 d()
          -    181
          +File ~/src/tools/go/pygolang-master/golang/__init__.py:179, in _GoFrame.__exit__(__goframe__, exc_type, exc_val, exc_tb)
          +    176     d = __goframe__.deferv.pop()
          +    178     # even if d panics - we have to call other defers
          +--> 179     with __goframe__:
          +    180         d()
          +    182 return __goframe__.recovered
      
          -~/src/tools/go/pygolang-master/golang/__init__.py in __exit__(__goframe__, exc_type, exc_val, exc_tb)
          -    177
          -    178             # even if d panics - we have to call other defers
          ---> 179             with __goframe__:
          -    180                 d()
          -    181
          +File ~/src/tools/go/pygolang-master/golang/__init__.py:179, in _GoFrame.__exit__(__goframe__, exc_type, exc_val, exc_tb)
          +    176     d = __goframe__.deferv.pop()
          +    178     # even if d panics - we have to call other defers
          +--> 179     with __goframe__:
          +    180         d()
          +    182 return __goframe__.recovered
      
          -~/src/tools/go/pygolang-master/golang/__init__.py in __exit__(__goframe__, exc_type, exc_val, exc_tb)
          -    178             # even if d panics - we have to call other defers
          -    179             with __goframe__:
          ---> 180                 d()
          -    181
          -    182         return __goframe__.recovered
          +File ~/src/tools/go/pygolang-master/golang/__init__.py:180, in _GoFrame.__exit__(__goframe__, exc_type, exc_val, exc_tb)
          +    178     # even if d panics - we have to call other defers
          +    179     with __goframe__:
          +--> 180         d()
          +    182 return __goframe__.recovered
      
          -~/src/tools/go/pygolang-master/golang/testprog/golang_test_defer_excchain.py in d3()
          -     33     1/0
          +File ~/src/tools/go/pygolang-master/golang/testprog/golang_test_defer_excchain.py:35, in d3()
                34 def d3():
           ---> 35     raise RuntimeError("d3: bbb")
          -     36
          -     37 @func
      
           RuntimeError: d3: bbb
      dfe75e9a
    • Kirill Smelkov's avatar
      golang: tests: Fix fmtargspec on py3.11 · c1e41ac8
      Kirill Smelkov authored
      Starting from python3.11 inspect.formatargspec was removed:
      
          f = <function test_func.<locals>.zzz at 0x7fb6d265fec0>
      
              def fmtargspec(f): # -> str
                  with warnings.catch_warnings():
                      warnings.simplefilter('ignore', DeprecationWarning)
          >           return inspect.formatargspec(*inspect.getargspec(f))
          E           AttributeError: module 'inspect' has no attribute 'formatargspec'
      
          golang/golang_test.py:1823: AttributeError
      
      We were already handling formatargspec deprecation in 45f4df9e (golang:
      tests: Silence inspect deprecation warning), but now is the time to
      actually replace its usage.
      
      -> Fix it by using `str(inspect.signature(f))` instead of
         `inspect.formatargspec(*inspect.getargspec(f))`.
      c1e41ac8
    • Kirill Smelkov's avatar
      pyx.build: Fix tests with recent setuptools · 304464e0
      Kirill Smelkov authored
      With setuptools 65 running test_pyx_build_cmdclass is failing because besides
      cmdclass_custom.py it sees nearby golang_pyx_user/ and golang_dso_user/ with
      top-project setups and rejects it:
      
          ---- 8< ----
          golang/pyx/build_test.py::test_pyx_build_cmdclass error: Multiple top-level packages discovered in a flat-layout: ['golang_pyx_user', 'golang_dso_user'].
      
          To avoid accidental inclusion of unwanted files or directories,
          setuptools will not proceed with this build.
      
          If you are trying to create a single distribution with multiple packages
          on purpose, you should not rely on automatic discovery.
          Instead, consider the following options:
      
          1. set up custom discovery (`find` directive with `include` or `exclude`)
          2. use a `src-layout`
          3. explicitly set `py_modules` or `packages` with a list of names
      
          To find more information, look for "package discovery" on setuptools docs.
          FAILED
      
          ================================ FAILURES =================================
          _________________________ test_pyx_build_cmdclass _________________________
      
              def test_pyx_build_cmdclass():
          >       _ = pyout(["cmdclass_custom.py", "build_ext"], cwd=testprog)
      
          golang/pyx/build_test.py:60:
          _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
          golang/golang_test.py:1742: in pyout
              return pyrun(argv, stdin=stdin, stdout=stdout, stderr=stderr, **kw)
          _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
      
          argv = ['cmdclass_custom.py', 'build_ext'], stdin = None, stdout = b'', stderr = None
          kw = {'cwd': '/home/kirr/src/tools/go/pygolang-master/golang/pyx/testprog'}, 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: cmdclass_custom.py build_ext
          E           (failed)
      
          golang/golang_test.py:1736: RuntimeError
          ---- 8< ----
      
      -> Work it around by defining py_modules explicitly as suggested in
      https://stackoverflow.com/a/72547402/9456786.
      304464e0
  4. 23 Jan, 2023 1 commit
    • Kirill Smelkov's avatar
      *: Replace usage of &vector[0] with vector.data() · 39dde7eb
      Kirill Smelkov authored
      We use &vector[0] in several places to get the pointer to vector data,
      but std::vector[i] states invariant that i must be < vector.size(). If
      the vector is not empty - then &vector[0] works ok. However if the
      vector is empty - then the invariant is broken.
      
      Normally it should not be a problem, since with empty vector we usually
      use (&vector[0], vector.size()) pair, and for empty vector its size is
      zero. In other words even if &vector[0] returns some not good pointer
      with zero size that data should never be accessed. But a build of
      libstdc++ could have asserts turned on and verify that i is actually <
      size. For example with default install on Fedora the following program
      with plain select()
      
          from golang import select
          select()
      
      crashes with assert triggered on the breakage of that invariant
      
          [Thread debugging using libthread_db enabled]
          Using host libthread_db library "/lib64/libthread_db.so.1".
          /usr/include/c++/10/bits/stl_vector.h:1042: std::vector<_Tp, _Alloc>::reference std::vector<_Tp, _Alloc>::operator[](std::vector<_Tp, _Alloc>::size_type) [with _Tp = golang::_selcase; _Alloc = std::allocator<golang::_selcase>; std::vector<_Tp, _Alloc>::reference = golang::_selcase&; std::vector<_Tp, _Alloc>::size_type = long unsigned int]: Assertion '__builtin_expect(__n < this->size(), true)' failed.
      
          Program received signal SIGABRT, Aborted.
          0x00007ffff7e22a25 in raise () from /lib64/libc.so.6
          (gdb) bt
          #0  0x00007ffff7e22a25 in raise () from /lib64/libc.so.6
          #1  0x00007ffff7e0b895 in abort () from /lib64/libc.so.6
          #2  0x00007fffea638d6d in std::__replacement_assert (__condition=0x7fffea63ea88 "__builtin_expect(__n < this->size(), true)",
              __function=0x7fffea63eab8 "std::vector<_Tp, _Alloc>::reference std::vector<_Tp, _Alloc>::operator[](std::vector<_Tp, _Alloc>::size_type) [with _Tp = golang::_selcase; _Alloc = std::allocator<golang::_selcase>; std::vector<_Tp, "..., __line=1042, __file=0x7fffea63ebe8 "/usr/include/c++/10/bits/stl_vector.h")
              at /usr/include/c++/10/x86_64-redhat-linux/bits/c++config.h:2552
          #3  std::vector<golang::_selcase, std::allocator<golang::_selcase> >::operator[] (this=<synthetic pointer>, __n=0)
              at /usr/include/c++/10/bits/stl_vector.h:1042
          #4  std::vector<golang::_selcase, std::allocator<golang::_selcase> >::operator[] (__n=0, this=<synthetic pointer>)
              at /usr/include/c++/10/bits/stl_vector.h:1040
          #5  __pyx_pf_6golang_7_golang_6pyselect (__pyx_self=<optimized out>, __pyx_v_pycasev=<optimized out>) at golang/_golang.cpp:7930
          #6  __pyx_pw_6golang_7_golang_7pyselect (__pyx_self=<optimized out>, __pyx_args=<optimized out>, __pyx_kwds=<optimized out>) at golang/_golang.cpp:7284
          #7  0x00007ffff7bceeab in cfunction_call_varargs (func=<built-in function pyselect>, args=(), kwargs=0x0)
              at /usr/src/debug/python3-3.8.10-1.fc32.x86_64/Objects/call.c:743
          ...
      
      -> Fix it all via replacing &vector[0] with vector.data() everywhere.
      
      No need to add new test since select() is used by test_blockforever()
      and running that test on Fedora was crashing as well.
      
      /reviewed-by @jerome
      /reviewed-at !22
      39dde7eb
  5. 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 nexedi/pygolang!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
  6. 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 nexedi/pygolang!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
  7. 26 Jan, 2022 7 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 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 !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 nexedi/pygolang!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 nexedi/pygolang!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 nexedi/pygolang!17
      07cae4e9