Repository: cython/cython

Issue:
Compiling a .py file with an augmenting .pxd leads to a mangled function name for cdef public function.
I would like to use the .py, with augmenting .pxd method for my Cython project so I can debug and use an IDE while I develop. For some reason this method leads to mangled exported function names. The definition of the function in the .pxd is ```cdef public int count_cwrap(int limit)``` and the .h file produced has this definition ```__PYX_EXTERN_C int __pyx_f_19pythagorean_triples_count_cwrap(int);``` If I convert the file to .pyx and add the cython definition, the name is not mangled anymore. I tried it and this occurs in both Linux and Windows.  I am attaching my source code to reproduce the issue.

pythagorean_triples.py:
```Python
# cython: language_level=3
import time
import cython
import numpy as np
 
def count(limit):
    if limit < 3:
       raise ValueError('Invalid limit value')
    arr = np.zeros(10)
    result : cython.int = 0
    a : cython.int
    b : cython.int
    c : cython.int
    for a in range(1, limit + 1):
        for b in range(a + 1, limit + 1):
            for c in range(b + 1, limit + 1):
                if c * c > a * a + b * b:
                    break
 
                if c * c == (a * a + b * b):
                    result += 1
    return result
	
def count_cwrap(limit):
    return count(limit)
 
if __name__ == '__main__':
    start = time.time()
    result = count(1000)
    duration = time.time() - start
    print(result, duration)
```
pythagorean_triples.pxd:
```Cython
# cython: language_level=3
cdef public int count_cwrap(int limit)
```
setup.py:
```Python
from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
 
setup(
    ext_modules = cythonize(Extension('pythagorean_triples', sources = ['pythagorean_triples.py']))
)
```


Is this a bug or am I doing something wrong perhaps in the setup.py?

I built with ```python setup_py.py build_ext --inplace``` in windows or with
```python3 setup_py.py build_ext --inplace``` in Linux

Thanks


Discussion:
Thanks for the report. Could be that the `private` marker is simply not copied when adapting the Python function signature from the `.pxd` file in `AlignFunctionDefinitions` syntax tree transformation (`ParseTreeTransforms.py`). Probably easy to add there. PR welcome.
[Here](https://github.com/cython/cython/blob/master/tests/build/module_api.srctree) is an example of a test file that you could borrow from. It's just a bunch of files stuffed into one that the test runner extracts.
Just ran into this on `3.0.0a10` and I don't know how to fix it. Has anyone been able to workaround this?

It seems `AlignFunctionDefinitions.visit_DefNode` is receiving a `pxd_def` with its name mangled despite having `visibility == 'public'`. https://github.com/cython/cython/blob/ddaaa7b8bfe9885b7bed432cd0a5ab8191d112cd/Cython/Compiler/ParseTreeTransforms.py#L2598

My gut feeling is that's probably incorrect, but I don't know the codebase well enough to say for certain.

I can see the `pxd_def` is generated in `ModuleScope.declare_cfunction` where it looks like the `(visibility == 'public' and defining)` condition is allowing it to be mangled.
https://github.com/cython/cython/blob/ddaaa7b8bfe9885b7bed432cd0a5ab8191d112cd/Cython/Compiler/Symtab.py#L1552


Also, is it possible to specify the visibility when using the `@cython.cfunc` decorator? Something like how `@cython.declare(visibility='public')` does?


Resolving patch:
diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py
--- a/Cython/Compiler/Symtab.py
+++ b/Cython/Compiler/Symtab.py
@@ -1617,7 +1617,7 @@ def declare_cfunction(self, name, type, pos,
         entry = self.lookup_here(name)
         if entry and entry.defined_in_pxd:
             if entry.visibility != "private":
-                mangled_cname = self.mangle(Naming.var_prefix, name)
+                mangled_cname = self.mangle(Naming.func_prefix, name)
                 if entry.cname == mangled_cname:
                     cname = name
                     entry.cname = cname


Test patch:
diff --git a/tests/compile/pxd_mangling_names.srctree b/tests/compile/pxd_mangling_names.srctree
new file mode 100644
--- /dev/null
+++ b/tests/compile/pxd_mangling_names.srctree
@@ -0,0 +1,46 @@
+# mode: compile
+# ticket: 2940
+
+PYTHON setup.py build_ext --inplace
+PYTHON -c "import a; a.test()"
+
+######## setup.py ########
+
+from Cython.Build import cythonize
+from Cython.Distutils.extension import Extension
+from distutils.core import setup
+
+setup(
+    ext_modules=cythonize([Extension("a", ["a.py", "b.c"])]),
+)
+
+######## a.pxd ########
+
+cdef public int foo()
+
+cdef extern from "b.h":
+    cpdef int bar()
+
+######## a.py ########
+
+def foo():
+    return 42
+
+def test():
+    assert bar() == 42
+
+######## b.h ########
+
+#ifndef B_H
+#define B_H
+
+int bar();
+
+#endif
+
+######## b.c ########
+
+#include "a.h"
+
+int bar() { return foo(); }
+
diff --git a/tests/pypy2_bugs.txt b/tests/pypy2_bugs.txt
--- a/tests/pypy2_bugs.txt
+++ b/tests/pypy2_bugs.txt
@@ -16,8 +16,9 @@ run.partial_circular_import
 # https://foss.heptapod.net/pypy/pypy/issues/3185
 run.language_level
 run.pure_pxd
+compile.pxd_mangling_names
 
-# Silly error with doctest matching slightly different string outputs rather than 
+# Silly error with doctest matching slightly different string outputs rather than
 # an actual bug but one I can't easily resolve
 run.with_gil