Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow cimport for quicktions #5

Open
wants to merge 25 commits into
base: master
Choose a base branch
from

Conversation

nocarryr
Copy link

Share the quicktions.Fraction extension type definitions with other Cython modules.

To make distribution possible from a pip install, the sources have to be included in the built wheels. The project layout prevents this (AFAIK) since only the compiled binaries would be included.

Placing quicktions into a sub-directory as a proper package (with __init__.py) seemed the best solution:

* src
  * quicktions
    * __init__.py
    * quicktions.pyx
    * quicktions.pxd

I realize this may be an undesirable change and can understand if it's not a direction you would like to go with this project.

Copy link
Owner

@scoder scoder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!
I think it's worth avoiding a package, though. Why not add quicktions.pxd to the package_data with package_data={'': ['quicktions.pxd']}) ? Could you maybe send a new (cleaned up) pull request for that, which avoids all the moving around of files?

def test_cimport(self):
self.build_test_module()
import pyximport
self.py_importer, self.pyx_importer = pyximport.install(inplace=True, language_level=3)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't use pyximport for the tests. It's really outdated.
cython.inline() might work for this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did look into that as it would be much cleaner, but from what I've tested, cimport isn't supported:

import cython
r = cython.inline('from quicktions cimport Fraction;return Fraction(a,b)', a=1,b=2)

Raises an exception:

AssertionError: Not yet supporting any cimports/includes from string code snippets

I agree that there should be a better way though

test_fractions.py Outdated Show resolved Hide resolved
test_fractions.py Outdated Show resolved Hide resolved
@@ -607,7 +586,7 @@ cdef class Fraction:
"Real numbers have no imaginary component."
return 0

def conjugate(self):
cpdef conjugate(self):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you really think this method is worth cpdef-ing?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably not since there's no typing or any other cython-related code there. It was mainly to reduce context switches between Python and Cython calls, but there's really not much going on in that method.

@@ -70,16 +70,6 @@ cdef pow10(Py_ssize_t i):

# Half-private GCD implementation.

cdef extern from *:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please keep this in here. It's strictly an internal implementation detail of the module.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am trying to remember if this being in the pyx caused compilation errors or not, but will test

src/quicktions/quicktions.pxd Show resolved Hide resolved
cdef cunumber _igcd(cunumber a, cunumber b)
cdef cunumber _ibgcd(cunumber a, cunumber b)
cdef _py_gcd(ullong a, ullong b)
cdef _gcd_fallback(a, b)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think any of these should be exported. They are really just implementation details and subject to change at any time.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly to above this may not be necessary. I'll do some testing

test_fractions.py Outdated Show resolved Hide resolved
cpdef limit_denominator(self, max_denominator=*)
cpdef conjugate(self)
cdef _eq(a, b)
cdef _richcmp(self, other, int op)
Copy link
Owner

@scoder scoder Jan 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it's possible to leave out these two (_eq() and _richcmp()). They are really not public, and they are also @final methods. In the worst case, we could turn them into functions instead of methods.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All methods and attributes in extension types must be declared in the pxd.

It doesn't alter the behavior outside of Cython, but it does allow the accelerated use of those methods by other Cython modules (whether directly or indirectly).

@nocarryr
Copy link
Author

Thanks!
I think it's worth avoiding a package, though. Why not add quicktions.pxd to the package_data with package_data={'': ['quicktions.pxd']}) ? Could you maybe send a new (cleaned up) pull request for that, which avoids all the moving around of files?

That was my initial endeavor, but I could never get the packaged wheel to include anything other than the compiled binary. This is expected due to the project not containing any valid packages. ("src layout", but no packages within "src/")
https://setuptools.readthedocs.io/en/latest/setuptools.html#using-a-src-layout

My prior (unsuccessful) attempts were here:
https://github.com/nocarryr/quicktions/tree/pxd-header

@@ -7,7 +7,7 @@ platform =
linux: linux
darwin: darwin
passenv = *
commands = coverage run --parallel-mode -m pytest test_fractions.py --capture=no --strict {posargs}
commands = coverage run --parallel-mode -m pytest --capture=no --strict {posargs}
coverage combine
coverage report -m --include=test_fractions.py
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should include the new test file.

with open(module_filename, 'w') as f:
f.write(module_code)

Cythonize.main(['-i', module_filename])
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can pass a (temporary) build directory here via -s build_dir=.... That should simplify the cleanup.

@cjdsellers
Copy link

Greetings,

I was wondering where we might be at with facilitating cimporting Fraction from quicktions?

@scoder
Copy link
Owner

scoder commented Sep 23, 2020

@cjdsellers difficult to say. To me, it feels like this PR is trying to do way too many things, but @nocarryr apparently got there by trying simpler things first, so I don't know. If there is a simple way to allow from quicktions cimport Fraction, then I'd be happy to get that included.

@nocarryr
Copy link
Author

@scoder I agree

Without changing the project's layout, it's not possible AFAIK to include and package the .pxd file so things are importable from pure Python and on Cython's search path.

I think I realized the simplicity of quicktions's layout (matching the Python stdlib as closely as possible) was more valuable than the (likely insignificant) performance increase of making it cimportable.

It would seem more beneficial to create that functionality as a completely separate project (which I've started working on here and there). That way it can be designed with much less GIL interaction required.

Should I just close this PR?

@cjdsellers
Copy link

Ok, I thought it might have been a simple fix with just writing a .pxd. However I understand there are other complexities here around the packaging, and I understand that one of the main values of the library is a drop in replacement for Fraction.

I'm looking for a high performance decimal type object which I can inherit from for C extension modules compiled with Cython. It would provide the base for some value type domain objects for a trading platform I've been developing. All production code has been written with Cython.

https://github.com/nautechsystems/nautilus_trader

I appreciate your response and look forward to anything you may come up with regarding the above.

@nocarryr
Copy link
Author

@scoder do you think cython/cython#2910 would be a viable alternative to this approach?

@scoder
Copy link
Owner

scoder commented Sep 25, 2020 via email

@scoder
Copy link
Owner

scoder commented Sep 25, 2020 via email

@cjdsellers
Copy link

Well, you can always define your own quicktions.pxd and use it. Cython does not care where the file came from, as long as it finds it somewhere on the Python import path.

I'll try that approach, thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants