I have the following directory structure:
testcython/
    setup.py
    testcython/
        __init__.py
        foo.pyx
        stuff.py
        bar/
            __init__.pxd
            __init__.py
            bar.pxd
            bar.pyx
where the file contents are as follows:
bar.pxd
# cython: language_level=3
cdef int square(int x)
bar.pyx
# cython: language_level=3
cdef int square(int x):
    return x * x
foo.pyx
# cython: language_level=3
import cython
cimport numpy as np
import numpy as np
from .Bar cimport square
def do_square(x):
    return square(x)
stuff.py
from __future__ import print_function
from .Foo import do_square
def do():
    print(do_square(2))
setup.py
import os, sys
from Cython.Build import build_ext, cythonize
from setuptools import setup, Extension, find_packages
def ext_modules():
    import numpy as np
    include_dirs = ['.', np.get_include()]
    root_dir = os.path.abspath(os.path.dirname(__file__))
    bar_ext = Extension(
        "Bar",
        sources=[root_dir + "/testcython/bar/bar.pyx"],
        include_dirs=include_dirs,
    )
    foo_ext = Extension(
        "Foo",
        sources=[root_dir + "/testcython/foo.pyx"],
        include_dirs=include_dirs
    )
    exts = [bar_ext, foo_ext]
    return cythonize(exts)
REQUIREMENTS = [
    "numpy",
    "cython"
]
setup(
    name="testcython",
    packages=find_packages(),
    ext_package="testcython",
    ext_modules=ext_modules(),
    cmdclass={"build_ext" : build_ext},
    zip_safe=False,
    install_requires=REQUIREMENTS
)
Question
The problem is that when I attempt to install this (with pip install -e . in the top testcython directory), I get the following errors from Cython:
Complete output from command python setup.py egg_info:
    Error compiling Cython file:
    ------------------------------------------------------------
    ...
    import cython
    cimport numpy as np
    import numpy as np
    from .Bar cimport square
    ^
    ------------------------------------------------------------
    testcython/foo.pyx:7:0: relative cimport beyond main package is not allowed
    Error compiling Cython file:
    ------------------------------------------------------------
    ...
    import numpy as np
    from .Bar cimport square
    def do_square(x):
        return square(x)
              ^
    ------------------------------------------------------------
This answer (cython: relative cimport beyond main package is not allowed) implies that including the root dir ('.') in the include_dirs argument(s) of the Extension objects should resolve the issue. 
Whilst this part of the Cython documentation mentions to use zip_safe=False in the args of setup when using the setuptools package.
As you can see from my setup.py file above, I have included both of these - yet I still receive the error above.
Note: If I change the name of the extensions (in Extension constructor) from Bar and Foo to testcython.Bar and testcython.Foo, respectively, then I get a different error:
Complete output from command python setup.py egg_info:
    Error compiling Cython file:
    ------------------------------------------------------------
    ...
    import cython
    cimport numpy as np
    import numpy as np
    from .Bar cimport square
    ^
    ------------------------------------------------------------
    testcython/foo.pyx:7:0: 'testcython/Bar/square.pxd' not found
    Error compiling Cython file:
    ------------------------------------------------------------
    ...
    import numpy as np
    from .Bar cimport square
    def do_square(x):
        return square(x)
              ^
    ------------------------------------------------------------
				
                        
I resolved this issue, with the help of a colleague, so I'll mention the solution here in case it helps people in the future.
The problem is related to how the Cython modules are imported, and more specifically - where the
.sofile is placed upon building the extension. Originally, theBar.sofile was generated in thetestcythondirectory - such that when attempting import from thebarsub-module, it couldn't find the corresponding shared object file.To solve this I needed to use the name
"bar.bar"when creating this extension, this then results in the.sofile being generated into thetestcython/bardirectory. Then, infoo.pyx, to use members from thisbarmodule the import had to be changed tofrom testcython.bar.bar cimport <name>.Note:
Additionally, the function
squareshown in the question cannot be used from another Cython module in this form as no__pyx_capi__is generated for freecdeffunctions. Instead, this function has to be wrapped in somecdefclass as a static method in order to use it from another Cython module, i.e.:Then this can be imported, in
foo.pyxfor example, withfrom testcython.bar.bar cimport Square. The classSquarethen essentially acts like a "namespace".