-
Notifications
You must be signed in to change notification settings - Fork 64
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
Revive the xNVMe Python language bindings #78
Conversation
fb36e0f
to
0e37cce
Compare
c857923
to
301d8c0
Compare
f1761b5
to
f5b03f6
Compare
toolbox/py_xnvme_pkg/Makefile
Outdated
.PHONY: generate-ctypes | ||
generate-ctypes: | ||
@echo "## py: gen" | ||
@clang2py ${XNVME_CAPI_HEADERS} > xnvme/ctypes_mapping.py |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had to specify additional arguments for macOS: @clang2py --clang-args="-I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk/usr/include/ -I../../include/" ${XNVME_CAPI_HEADERS} > xnvme/ctypes_mapping.py
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, curious. This is probably something to explore further. To get this moving along then the build-testing is restricted to a single platform, the package generated and then tested on all the usual suspects.
436c265
to
468ec55
Compare
e8f5baf
to
6329319
Compare
50c5024
to
cf86481
Compare
python/bin/xpy_dev_open
Outdated
|
||
import xnvme.ctypes_mapping as capi | ||
|
||
capi.guard_unloadable() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better to move this to the library.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about this would go on the list of things to refine in docs/python/index.rst
? Have done so now.
python/bin/xpy_enumerate
Outdated
|
||
import xnvme.ctypes_mapping as capi | ||
|
||
capi.guard_unloadable() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about this would go on the list of things to refine in docs/python/index.rst
? Have done so now.
python/setup.py
Outdated
package_dir={"": "./"}, | ||
packages=setuptools.find_packages(where="./"), | ||
python_requires=">=3.6", | ||
install_requires="pytest", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Install wouldn't require pytest
. Either use tests_require
or now that you have a python/pyproject.toml
, put it there instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have gone with the approach of installing the testcases, this allows the ease-of-use of running the pytest via the package-discovery. This is why pytest is added as a runtime requirement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still think we should not have it here, and put it in either tests_require
or pyproject.toml
, as it's not part of normal runtime usage.
Also, isn't it supposed to be a list? ["pytest"]
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By having it as install_requires
then pytest is installed along with the package.
By doing so and by adding the library-tests with the package, then it becomes incredibly easy to verify that the bindings are working as intended by invoking:
python3 -m pytest --pyargs xnvme
I don't see any reason not to do this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason not to do it, would be because it's not a runtime dependency. I think it would be exceedingly rare somebody else is running our test suite. And as soon as we merge my Cython PR, the tests would no longer succeed without proper configuration.
But why not put it under the test requirements, if it's for testing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding to that, this is why python3 -m pytest --pyargs xnvme
is added as a bold note in the docs right after installation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, this does not dictate that all tests are installed with the package, just the ones useful for the use-case described above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about a different approach? Instead of implementing these as a pytest, why not just a simple function?
def self_test():
# Self-test for the dynamically loaded library
# The 'capi' is loaded upon module access, this checks that it succeeded
assert capi.is_loaded, "C API isn't loaded"
# This verifies that the loaded library actually is the xNVMe library, by checking for
# the existance of a xNVMe specific funtion.
assert capi.xnvme_enumerate, "xnvme_enumerate is not present in library. Library isn't loaded correctly"
# Verifies that at at least one function is callable
assert capi.xnvme_libconf_pr(capi.XNVME_PR_DEF), "Failed to call function 'xnvme_libconf_pr in xNVMe. Library isn't loaded correctly"
print('xNVMe ctypes bindings loaded correctly!')
Then in the docs: python3 -c 'import xnvme.ctypes_bindings as xnvme; xnvme.self_test()'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is also an approach, the down-side is that then the tests need to added in multiple places and not behaving as nicely as pytest
.
I do like the messages on the asserts, so I have added that to the tests.
I don't see any reason not to use pytest
for this, the only counter-argument is that it is "unusual" but it is no more unusual than pytest
describing the use-case in their documentation and providing test-collection for it. So, its a well-documented and supported feature of pytest
.
Should the addition of pytest
cause us grief, then it is no worse than we can remove it and replace it with something like the approach above. However, for now I see no reason to, it only makes things simpler/easier to use.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the only counter-argument is that it is "unusual"
The other argument is that this solution is short-lived. As soon as we merge my Cython-to-Python bindings, it won't work anymore.
Should the addition of pytest cause us grief, then it is no worse than we can remove it and replace it with something like the approach above.
If you want to, then keep it and let's see.
@Baekalfen I have addressed your feedback, some with changes, some with clarification, and two things post-poned as "nice-to-have" / added to |
Signed-off-by: Simon A. F. Lund <simon.lund@samsung.com>
Signed-off-by: Simon A. F. Lund <simon.lund@samsung.com>
388c25c
to
1010dad
Compare
@Baekalfen found some of your comments on the commits. Addressed those as well and pushed. Thanks for all the input! Your review improved this PR in a lot of ways:
Thank you! |
The package requires Python 3.6+, attempting to not go beyond 3.6+ due to lack of availability on a range of Linux distributions. * Makefile: misc. utilities for working with the Python package * The Python package itself - 'python/xnvme' home of the Python package - 'python/xnvme/library_loader.py', provides helpers used for autoloading the xNVMe shared library - 'python/xnvme/ctypes_header.py', autogenerated ctypes encapsulation of the xNVMe C API * Examples - Provided as executables and bundled with the Python package - Available in: 'python/bin/' - Serves as stand-alone usage-examples * Tests - Provided with the Python xnvme package - Available in 'python/xnvme/tests' - Runnable with: 'python3 -m pytest --pyargs xnvme' - Requires pytest * git-ignore: - Added the generated headers, don't want them as part of the repos * pcf: exclude the py-ctypes-mapping from flake8 The remaining files: LICENSE/README.rst/setup.py/pyproject.toml, as the "usual" suspects for a Python package. Signed-off-by: Simon A. F. Lund <simon.lund@samsung.com>
With the revival of the Python language bindings, documentation is needed to describe what they are, how to use them, and the state they are in. This extension to the documentation does so. Signed-off-by: Simon A. F. Lund <simon.lund@samsung.com>
This adds a workflow-job named 'build-python' building the Python language bindings and emitting an installable Python package as a build-artifact named 'python-pkg' / xnvme-py-MAJOR-MINOR-PATCH.tar.gz. The workflow-job, runs before 'build-linux' and 'build-macos' as these depend on it to consume the 'python-pkg' for testing. The downside is that the workflow-pipeline is delayed the time it takes to process the job which is ~30 seconds, arguable this time is reclaimed for the total pipeline to finish since the 'build-python' step runs Alpine and the various tools needed are deployed there much faster than on the other Linux distributions and Linux. A actual shortcoming is that the build of the Python package is only tested in a single environment. However, the main motivation is that the CI emits a single package which is then tested on the various targets, this package can then be promoted for release. Additionally, the package will be tested based on only on the runtime-dependencies defined in (setup.py) and verified that it runs without all the packages defined in requirements.txt. Signed-off-by: Simon A. F. Lund <simon.lund@samsung.com>
Thanks @Baekalfen ! |
xNVMe previously had Python bindings, however, these where manually written wrappers which were not properly tested, thus they became stale and were removed from the repository.
However, recently when providing examples of how to dynamically load xNVMe from C and Python, then it seemed worth-while to explore just how much is possible via
ctypes
. With a couple of non-intrusive changes to the public C API (adding a couple of includes), then it is possible to auto-generate a complete ctypes-wrapping for the C API. With the essentials auto-generated, then usingctypes
is a breeze.With this PR a full xNVMe C API mapping is available along with automatic dynamic loading. This module can serve for those wanting to experiment with the API from the comforts of Python. Additionally, as a reference for adding Cython bindings as well, for those going beyond poking at the API.
Have a look at the
toolbox/py_xnvme_pkg/README.rst
for more information.EDIT: Have a look at the docs in
docs/python/index.rst
for more information.