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

[PROTO] Adding Cython framework for lite-functions (and more) #1416

Draft
wants to merge 16 commits into
base: cython
Choose a base branch
from

Conversation

rocco8773
Copy link
Member

This is currently a prototype to see how we can incorporate Cython into plasmapy. I'm particularly curious in how...

  1. To write cython code. (Using lite-functions as the prototype)
  2. How to package and distribute with cython code.
  3. How to develop with cython code.

Using cython in plasmapy means developers/contributors have an extra step to get off the ground, because, like c, cython code needs to be compiled. Cython looks very similar to Python and can be written in either .py files or .pyx files. .pyx files are specifically Cython files. Once this is done, the code needs to be cythonized (i.e. built/compile). In this process c code is generated from the source code and then the c code is compiled into share object files. Python can then import directly from these shared object files. However, these shared object files are platform specific and this is why we can not package with pre-compiled code. Luckily, these shared object files are automatically generate with pip installs, so there shouldn't be any extra steps for end users. For developers/contributors, we don't typically pip install so we have manually create the shared object files. This is done by...

  1. Navigating to the root directory for plasmapy.
  2. Executing the following command
    python setup.py build_ext --inplace

This will create the shared object files in your repository. However, these are not under version control (i.e. excluded by .gitignore), so whenever cython code is modified or you switch to a new branch you'll likely need to re-compile.

To assist in cleaning up the compiled code I did modify the setup.py so that following commands will remove files generated by python setup.py build_ext --inplace.

python setup.py clean
python setup.py clean --all

If you want to see what file are deleted before cleaning, then you can use...

python setup.py clean --dry-run

@codecov
Copy link

codecov bot commented Feb 3, 2022

Codecov Report

Merging #1416 (e343360) into cython (a624976) will not change coverage.
The diff coverage is n/a.

❗ Current head e343360 differs from pull request most recent head 0fb4c75. Consider uploading reports for the commit 0fb4c75 to get more accurate results

Impacted file tree graph

@@           Coverage Diff           @@
##           cython    #1416   +/-   ##
=======================================
  Coverage   97.15%   97.15%           
=======================================
  Files          77       77           
  Lines        7406     7406           
=======================================
  Hits         7195     7195           
  Misses        211      211           

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update a624976...0fb4c75. Read the comment docs.

@github-actions github-actions bot added the CI Related to continuous integration label Feb 3, 2022
Copy link
Member

@namurphy namurphy left a comment

Choose a reason for hiding this comment

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

Looking good! My suggested changes are all minor, and you're free to ignore them if you so choose.

The main thing I'm still wondering about, which I think we talked about yesterday, is on whether or not it specifically needs to be a numpy array as input, or if it could be a real or int. I think I remember you saying that yes, it does need to be a numpy array for the input.

If you're looking to eventually merge this prototype, we'd want to include the additional required steps in the contributor guide section on installing PlasmaPy for development.

Thank you for doing this!

Comment on lines +11 to +13
Z = 1j * np.sqrt(np.pi) * wofz(zeta)

return Z
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Z = 1j * np.sqrt(np.pi) * wofz(zeta)
return Z
return 1j * np.sqrt(np.pi) * wofz(zeta)

[optional] This is a sort of refactoring I've often had pop up lately. Up to you.

setup.cfg Show resolved Hide resolved
setup.py Outdated
Comment on lines 56 to 73
from importlib.machinery import EXTENSION_SUFFIXES

ext_suffixes = EXTENSION_SUFFIXES.copy()
ext_suffixes.append(".c")

cy_files = get_cython_files()
for file in cy_files:
for ext in ext_suffixes:
ext_file = file.with_suffix(ext)
if not ext_file.exists():
continue

print(f"removing '{ext_file}'")

if "--dry-run" in sys.argv:
continue

ext_file.unlink() # delete extension file
Copy link
Member

Choose a reason for hiding this comment

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

[optional] My inclination would be to put this into its own function that gets called here, so as to not have implementation details in the if __name__ == "main" part. This goes along with the idea of having a main() function that gets called in this section of code. In this case there isn't much else here, so I'd have no problem leaving it as is. Up to you.


def get_cython_files():
"""Find all cython files (*.pyx) in plasmapy."""
return list(Path().glob("plasmapy/**/*.pyx"))
Copy link
Member

Choose a reason for hiding this comment

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

I hadn't seen the ** before, so I had to look up on the World Wide Web™ to learn that it mean multiple directories here. Glad to learn new Python!

Comment on lines +15 to +17
# def plasma_dispersion_func_deriv(
# zeta: Union[complex, int, float, np.ndarray, u.Quantity]
# ) -> Union[complex, float, np.ndarray, u.Quantity]:
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
# def plasma_dispersion_func_deriv(
# zeta: Union[complex, int, float, np.ndarray, u.Quantity]
# ) -> Union[complex, float, np.ndarray, u.Quantity]:

setup.py Outdated
# by:
# python setup.py build_ext --inplace
#
# this get executed with the command
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
# this get executed with the command
# this gets executed with the command

@rocco8773 rocco8773 changed the base branch from main to cython February 8, 2022 19:59
Copy link
Member

@StanczakDominik StanczakDominik left a comment

Choose a reason for hiding this comment

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

LGTM, one potential idea to make this introduce less code!

# this get executed with the command
# python setup.py clean
#
if "clean" in sys.argv:
Copy link
Member

Choose a reason for hiding this comment

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

clean is already a setup.py command and I would be wary of reimplementing it. Have you tried playing around with https://stackoverflow.com/questions/30359216/compile-a-cython-project-and-clean and see whether the default python setup.py clean --all does the trick?

People are generally moving away from setup.py in the python space in general... but that's probably an issue for another day.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not overriding python setup.py clean, this still runs as expected. I'm adding an additional setup after the clean command is executed. The clean command does not know about, and thus can not remove, --inplace shared object files. All this is doing is seeing if the clean command is specified, letting the normal clean run, and then our setup.py removes the --inplace files.

Copy link
Member

Choose a reason for hiding this comment

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

Oooooh okay then, gotcha. 👍🏻

Comment on lines +147 to +148
pip install --progress-bar off twine
pip install --progress-bar off -r requirements/build.txt
Copy link
Member

Choose a reason for hiding this comment

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

good cleanup! 👍🏻

@rocco8773 rocco8773 marked this pull request as draft February 10, 2022 19:05
@github-actions
Copy link

This pull request will be closed in 14 days due to a year of inactivity unless the stale label or comment is removed.

@github-actions github-actions bot added the Stale Dormant issues & PRs which will be automatically closed if the label is not removed. label May 14, 2023
@namurphy namurphy added the status: dormant PRs that are stalled label May 26, 2023
@github-actions github-actions bot removed the Stale Dormant issues & PRs which will be automatically closed if the label is not removed. label Sep 9, 2023
@namurphy namurphy added this to the Future milestone Apr 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CI Related to continuous integration status: dormant PRs that are stalled
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants