Skip to content

Commit 0f7e38e

Browse files
authored
Several big improvements (#55)
* Separate linting from testing pipeline. Run testing pipeline for all live versions of Python. * Testing on osx. * Use strategy matrix for os. * setup-miniconda action. * Run isort. * Specify environment file for setup-miniconda. * mamba for CI, specify shell. * Fix environment name in CI yaml. * Fix isort/black conflict. * Remove subprocess.run check_output parameter for compatibility with Python 3.6 * Fix black issue. Add pre-commit. * Update precommit package versions. * Remove Windows CI, fix Mac + python 3.6 to use libc++ instead libstdc++. * Apply CFLAGS to pytest, not the install... oops! * Remove cpprun. It was never a good idea. * Refactoring and building ARCHITECTURE.md * Contributing and minor function renamings. * Replace config module with settings dictionary. * Add coverage to CI. * XML coverage report. * Missed coverage on the mac tests. * Update README.
1 parent 65dc9ed commit 0f7e38e

26 files changed

+670
-537
lines changed

.coveragerc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[run]
2+
source=cppimport

.github/workflows/lint_and_test.yml

Lines changed: 0 additions & 34 deletions
This file was deleted.

.github/workflows/test.yml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
name: Test
2+
3+
on: [push]
4+
5+
jobs:
6+
test:
7+
strategy:
8+
fail-fast: false
9+
matrix:
10+
os: ["ubuntu-latest", "macos-latest"]
11+
python-version: ["3.6", "3.7", "3.8", "3.9"]
12+
name: Test (${{ matrix.python-version }}, ${{ matrix.os }})
13+
runs-on: ${{ matrix.os }}
14+
defaults:
15+
run:
16+
shell: bash -l {0}
17+
steps:
18+
- uses: actions/checkout@v2
19+
- uses: conda-incubator/setup-miniconda@v2
20+
with:
21+
mamba-version: "*"
22+
channels: conda-forge
23+
activate-environment: cppimport
24+
environment-file: environment.yml
25+
python-version: ${{ matrix.python-version }}
26+
- name: Install cppimport
27+
run: |
28+
pip install --no-use-pep517 --no-deps --disable-pip-version-check -e .
29+
- name: Lint with flake8
30+
run: |
31+
flake8 .
32+
- name: Check formatting with black
33+
run: |
34+
black --check .
35+
- name: Check import ordering with isort
36+
run: |
37+
isort --check .
38+
- name: Test
39+
if: ${{ matrix.os == 'macos-latest' }}
40+
run: |
41+
CFLAGS='-stdlib=libc++' pytest --cov=./ --cov-report=xml
42+
- name: Test
43+
if: ${{ matrix.os != 'macos-latest' }}
44+
run: |
45+
pytest --cov=./ --cov-report=xml
46+
- name: Upload coverage to Codecov
47+
uses: codecov/codecov-action@v1
48+
with:
49+
fail_ci_if_error: true

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
*.so
44
*.cppimporthash
55
.rendered.*
6-
.cpprunfiles
76
__pycache__
87
build
98
cppimport.egg-info
109
dist
1110
.cache
1211
.tox
12+
.mypy_cache/
13+
.coverage
14+
htmlcov

.pre-commit-config.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
repos:
2+
- repo: https://github.com/psf/black
3+
rev: 20.8b1
4+
hooks:
5+
- id: black
6+
language_version: python3
7+
- repo: https://gitlab.com/pycqa/flake8
8+
rev: 3.8.4
9+
hooks:
10+
- id: flake8
11+
- repo: https://github.com/pycqa/isort
12+
rev: 5.7.0
13+
hooks:
14+
- id: isort
15+
name: isort (python)

CONTRIBUTING.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Contributing
2+
3+
When contributing to this repository, feel free to add an issue or pull request! There really aren't any rules, but if you're mean, I'll be sad. I'm happy to collaborate on pull requests if you would like. There's no need to submit a perfect, finished product.
4+
5+
To install in development mode and run the tests:
6+
```
7+
git clone [email protected]:tbenthompson/cppimport.git
8+
cd cppimport
9+
conda env create
10+
conda activate cppimport
11+
pre-commit install
12+
pip install --no-use-pep517 --disable-pip-version-check -e .
13+
pytests
14+
```
15+
16+
Helpful checklist for a pull request:
17+
18+
* Run the tests! Check that the CI workflows are passing.
19+
* Update `README.md` and `CONTRIBUTING.md` with any relevant changes.
20+
21+
# Architecture
22+
23+
## Entrypoints:
24+
25+
The main entrypoint for cppimport is the `cppimport.import_hook` module, which interfaces with the Python importing system to allow things like `import mycppfilename`. For a C++ file to be a valid import target, it needs to have the word `cppimport` in its first line. Without this first line constraint, it is possible for the importing system to cause imports in other Python packages to fail. Before adding the first-line constraint, the cppimport import_hook had the unfortunate consequence of breaking some scipy modules that had adjacent C and C++ files in the directory tree.
26+
27+
There is an alternative, and more explicit interface provided by the `imp`, `imp_from_filepath` and `build` functions here.
28+
* `imp` does exactly what the import hook does except via a function so that instead of `import foomodule` we would do `foomodule = imp('foomodule')`.
29+
* `imp_from_filepath` is even more explicit, allowing the user to pass a C++ filepath rather than a modulename. For example, `foomodule = imp('../cppcodedir/foodmodule.cpp')`. This is rarely necessary but can be handy for debugging.
30+
* `build` is similar to `imp` except that the library is only built and not actually loaded as a Python module.
31+
32+
`imp`, `imp_from_filepath` and `build` are in the `__init__.py` to separate external facing API from the guts of the package that live in internal submodules.
33+
34+
## What happens when we import a C++ module.
35+
36+
1. First the `cppimport.find.find_module_cpppath` function is used to find a C++ file that matches the desired module name.
37+
2. Next, we determine if there's already an existing compiled extension that we can use. If there is, the `cppimport.importer.is_build_needed` function is used to determine if the extension is up to date with the current code. If the extension is up to date, we attempt to load it. If the extension is loaded successfully, we return the module and we're done! However, if for whichever reason, we can't load an existing extension, we need to build the extension, a process directed by `cppimport.importer.template_and_build`.
38+
3. The first step of building is to run the C++ file through the Mako templating system with the `cppimport.templating.run_templating` function. The main purpose of this is to allow users to embed configuration information into their C++ file. Without some sort of similar mechanism, there would be no way of passing information to build system because the `import modulename` statement can't carry information. The templating serves a secondary benefit in that simple code generation can be performed if needed. However, most users probably stick to a simple header or footer similar to the one demonstrated in the README.
39+
4. Next, we use setuptools to build the C++ extension using `cppimport.build_module.build_module`. This function calls setuptools with the appropriate arguments to build the extension in place next to the C++ file in the directory tree.
40+
5. Next, we call `cppimport.checksum.checksum_save` to add a hash of the appended contents of all relevant source and header files. This checksum is appended to the end of the `.so` or `.dylib` file. This seems legal according to specifications and, in practice, causes no problems.
41+
6. Finally, the compiled and loaded extension module is returned to the user.
42+
43+
## Useful links
44+
45+
* PEP 302 that made this possible: https://www.python.org/dev/peps/pep-0302/
46+
* The gory details of Python importing: https://docs.python.org/3/reference/import.html

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
The MIT License (MIT)
22

3-
Copyright (c) 2020 T. Ben Thompson
3+
Copyright (c) 2021 T. Ben Thompson
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

MANIFEST.in

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
include tox.ini
21
include README.md
32
include VERSION
43
recursive-include tests *

0 commit comments

Comments
 (0)