├── requirements.txt ├── docs ├── toc.rst ├── _static │ └── gj-logo.png ├── Makefile ├── make.bat ├── conf.py └── index.rst ├── .gitignore ├── c2f.py ├── setup.py ├── .github └── workflows │ └── release.yml └── README.rst /requirements.txt: -------------------------------------------------------------------------------- 1 | Cython 2 | Sphinx 3 | -------------------------------------------------------------------------------- /docs/toc.rst: -------------------------------------------------------------------------------- 1 | .. toctree:: 2 | :hidden: 3 | 4 | index 5 | -------------------------------------------------------------------------------- /docs/_static/gj-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grantjenks/python-c2f/HEAD/docs/_static/gj-logo.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[co] 2 | *.so 3 | *.dSYM 4 | *.c 5 | 6 | .DS_Store 7 | 8 | env*/ 9 | _build/ 10 | build/ 11 | c2f.egg-info/ 12 | dist/ 13 | -------------------------------------------------------------------------------- /c2f.py: -------------------------------------------------------------------------------- 1 | "Celsius to Fahrenheit Library" 2 | 3 | def convert(celsius: float) -> float: 4 | "Convert Celsius to Fahrenheit" 5 | fahrenheit = celsius * 1.8 + 32 6 | return fahrenheit 7 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | from Cython.Build import cythonize 3 | 4 | setup( 5 | name='c2f', 6 | version='1.0.5', 7 | py_modules=['c2f'], 8 | ext_modules=cythonize('c2f.py'), 9 | long_description=open('README.rst').read(), 10 | ) 11 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | # import os 14 | # import sys 15 | # sys.path.insert(0, os.path.abspath('.')) 16 | 17 | 18 | # -- Project information ----------------------------------------------------- 19 | 20 | project = 'Cython for All with GitHub Actions' 21 | copyright = '2019, Grant Jenks' 22 | author = 'Grant Jenks' 23 | 24 | # The full version, including alpha/beta/rc tags 25 | release = '1.0' 26 | 27 | 28 | # -- General configuration --------------------------------------------------- 29 | 30 | # Add any Sphinx extension module names here, as strings. They can be 31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 32 | # ones. 33 | extensions = [ 34 | ] 35 | 36 | # Add any paths that contain templates here, relative to this directory. 37 | templates_path = ['_templates'] 38 | 39 | # List of patterns, relative to source directory, that match files and 40 | # directories to ignore when looking for source files. 41 | # This pattern also affects html_static_path and html_extra_path. 42 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 43 | 44 | 45 | # -- Options for HTML output ------------------------------------------------- 46 | 47 | # The theme to use for HTML and HTML Help pages. See the documentation for 48 | # a list of builtin themes. 49 | # 50 | html_theme = 'alabaster' 51 | 52 | # Add any paths that contain custom static files (such as style sheets) here, 53 | # relative to this directory. They are copied after the builtin static files, 54 | # so a file named "default.css" will overwrite the builtin "default.css". 55 | html_static_path = ['_static'] 56 | 57 | # Theme options are theme-specific and customize the look and feel of a theme 58 | # further. For a list of options available for each theme, see the 59 | # documentation. 60 | html_theme_options = { 61 | 'logo': 'gj-logo.png', 62 | 'logo_name': True, 63 | 'logo_text_align': 'center', 64 | 'analytics_id': 'UA-19364636-2', 65 | 'show_powered_by': False, 66 | } 67 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | push: 5 | tags: 6 | - v* 7 | 8 | jobs: 9 | 10 | build-linux-cp35: 11 | runs-on: ubuntu-latest 12 | container: quay.io/pypa/manylinux2014_x86_64 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - name: Install Python package dependencies 18 | run: /opt/python/cp35-cp35m/bin/python -m pip install cython wheel 19 | 20 | - name: Build binary wheel 21 | run: /opt/python/cp35-cp35m/bin/python setup.py bdist_wheel 22 | 23 | - name: Apply auditwheel 24 | run: auditwheel repair -w dist dist/* 25 | 26 | - name: Remove linux wheel 27 | run: rm dist/*-linux_x86_64.whl 28 | 29 | - name: Archive dist artifacts 30 | uses: actions/upload-artifact@v2 31 | with: 32 | name: dist-linux-3.5 33 | path: dist 34 | 35 | build-linux-cp36: 36 | runs-on: ubuntu-latest 37 | container: quay.io/pypa/manylinux2014_x86_64 38 | 39 | steps: 40 | - uses: actions/checkout@v2 41 | 42 | - name: Install Python package dependencies 43 | run: /opt/python/cp36-cp36m/bin/python -m pip install cython wheel 44 | 45 | - name: Build binary wheel 46 | run: /opt/python/cp36-cp36m/bin/python setup.py bdist_wheel 47 | 48 | - name: Apply auditwheel 49 | run: auditwheel repair -w dist dist/* 50 | 51 | - name: Remove linux wheel 52 | run: rm dist/*-linux_x86_64.whl 53 | 54 | - name: Archive dist artifacts 55 | uses: actions/upload-artifact@v2 56 | with: 57 | name: dist-linux-3.6 58 | path: dist 59 | 60 | build-linux-cp37: 61 | runs-on: ubuntu-latest 62 | container: quay.io/pypa/manylinux2014_x86_64 63 | 64 | steps: 65 | - uses: actions/checkout@v2 66 | 67 | - name: Install Python package dependencies 68 | run: /opt/python/cp37-cp37m/bin/python -m pip install cython wheel 69 | 70 | - name: Build binary wheel 71 | run: /opt/python/cp37-cp37m/bin/python setup.py bdist_wheel 72 | 73 | - name: Apply auditwheel 74 | run: auditwheel repair -w dist dist/* 75 | 76 | - name: Remove linux wheel 77 | run: rm dist/*-linux_x86_64.whl 78 | 79 | - name: Archive dist artifacts 80 | uses: actions/upload-artifact@v2 81 | with: 82 | name: dist-linux-3.7 83 | path: dist 84 | 85 | build-linux-cp38: 86 | runs-on: ubuntu-latest 87 | container: quay.io/pypa/manylinux2014_x86_64 88 | 89 | steps: 90 | - uses: actions/checkout@v2 91 | 92 | - name: Install Python package dependencies 93 | run: /opt/python/cp38-cp38/bin/python -m pip install cython wheel 94 | 95 | - name: Build binary wheel 96 | run: /opt/python/cp38-cp38/bin/python setup.py bdist_wheel 97 | 98 | - name: Apply auditwheel for manylinux wheel 99 | run: auditwheel repair -w dist dist/* 100 | 101 | - name: Remove linux wheel 102 | run: rm dist/*-linux_x86_64.whl 103 | 104 | - name: Archive dist artifacts 105 | uses: actions/upload-artifact@v2 106 | with: 107 | name: dist-linux-3.8 108 | path: dist 109 | 110 | build-macos: 111 | runs-on: macos-latest 112 | strategy: 113 | max-parallel: 4 114 | matrix: 115 | python-version: [3.5, 3.6, 3.7, 3.8] 116 | 117 | steps: 118 | - uses: actions/checkout@v2 119 | 120 | - name: Set up Python ${{ matrix.python-version }} x64 121 | uses: actions/setup-python@v2 122 | with: 123 | python-version: ${{ matrix.python-version }} 124 | architecture: x64 125 | 126 | - name: Install Python package dependencies 127 | run: pip install cython wheel 128 | 129 | - name: Build binary wheel 130 | run: python setup.py bdist_wheel 131 | 132 | - name: Archive dist artifacts 133 | uses: actions/upload-artifact@v2 134 | with: 135 | name: dist-macos-${{ matrix.python-version }} 136 | path: dist 137 | 138 | build-windows: 139 | runs-on: windows-latest 140 | strategy: 141 | max-parallel: 3 142 | matrix: 143 | python-version: [3.6, 3.7, 3.8] 144 | 145 | steps: 146 | - uses: actions/checkout@v2 147 | 148 | - name: Download Build Tools for Visual Studio 2019 149 | run: Invoke-WebRequest -Uri https://aka.ms/vs/16/release/vs_buildtools.exe -OutFile vs_buildtools.exe 150 | 151 | - name: Run vs_buildtools.exe install 152 | run: ./vs_buildtools.exe --quiet --wait --norestart --nocache --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --add Microsoft.VisualStudio.Component.VC.v141.x86.x64 --add Microsoft.VisualStudio.Component.VC.140 --includeRecommended 153 | 154 | - name: Set up Python ${{ matrix.python-version }} x64 155 | uses: actions/setup-python@v2 156 | with: 157 | python-version: ${{ matrix.python-version }} 158 | architecture: x64 159 | 160 | - name: Install Python package dependencies 161 | run: pip install cython wheel 162 | 163 | - name: Build binary wheel 164 | run: python setup.py bdist_wheel 165 | 166 | - name: Archive dist artifacts 167 | uses: actions/upload-artifact@v2 168 | with: 169 | name: dist-windows-${{ matrix.python-version }} 170 | path: dist 171 | 172 | upload: 173 | needs: [build-linux-cp35, build-linux-cp36, build-linux-cp37, build-linux-cp38, build-macos, build-windows] 174 | runs-on: ubuntu-latest 175 | 176 | steps: 177 | - uses: actions/checkout@v2 178 | 179 | - name: Set up Python 180 | uses: actions/setup-python@v2 181 | with: 182 | python-version: 3.8 183 | 184 | - name: Install dependencies 185 | run: | 186 | python -m pip install --upgrade pip 187 | pip install -r requirements.txt 188 | 189 | - name: Create source dist 190 | run: python setup.py sdist 191 | 192 | - name: Stage linux 3.5 193 | uses: actions/download-artifact@v2 194 | with: 195 | name: dist-linux-3.5 196 | - run: mv -v dist-linux-3.5/* dist/ 197 | 198 | - name: Stage linux 3.6 199 | uses: actions/download-artifact@v2 200 | with: 201 | name: dist-linux-3.6 202 | - run: mv -v dist-linux-3.6/* dist/ 203 | 204 | - name: Stage linux 3.7 205 | uses: actions/download-artifact@v2 206 | with: 207 | name: dist-linux-3.7 208 | - run: mv -v dist-linux-3.7/* dist/ 209 | 210 | - name: Stage linux 3.8 211 | uses: actions/download-artifact@v2 212 | with: 213 | name: dist-linux-3.8 214 | - run: mv -v dist-linux-3.8/* dist/ 215 | 216 | - name: Stage macos 3.5 217 | uses: actions/download-artifact@v2 218 | with: 219 | name: dist-macos-3.5 220 | - run: mv -v dist-macos-3.5/* dist/ 221 | 222 | - name: Stage macos 3.6 223 | uses: actions/download-artifact@v2 224 | with: 225 | name: dist-macos-3.6 226 | - run: mv -v dist-macos-3.6/* dist/ 227 | 228 | - name: Stage macos 3.7 229 | uses: actions/download-artifact@v2 230 | with: 231 | name: dist-macos-3.7 232 | - run: mv -v dist-macos-3.7/* dist/ 233 | 234 | - name: Stage macos 3.8 235 | uses: actions/download-artifact@v2 236 | with: 237 | name: dist-macos-3.8 238 | - run: mv -v dist-macos-3.8/* dist/ 239 | 240 | - name: Stage windows 3.6 241 | uses: actions/download-artifact@v2 242 | with: 243 | name: dist-windows-3.6 244 | - run: mv -v dist-windows-3.6/* dist/ 245 | 246 | - name: Stage windows 3.7 247 | uses: actions/download-artifact@v2 248 | with: 249 | name: dist-windows-3.7 250 | - run: mv -v dist-windows-3.7/* dist/ 251 | 252 | - name: Stage windows 3.8 253 | uses: actions/download-artifact@v2 254 | with: 255 | name: dist-windows-3.8 256 | - run: mv -v dist-windows-3.8/* dist/ 257 | 258 | - name: Upload with twine 259 | env: 260 | TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }} 261 | TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }} 262 | run: | 263 | ls -l dist/* 264 | pip install twine 265 | twine upload dist/* 266 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Cython for All with GitHub Actions 2 | ================================== 3 | 4 | Is Python an interpreted or compiled language? Trick question. It’s actually 5 | both. With tools like Cython, we can take the compilation step further and 6 | remove the interpreter loop almost entirely. Cython produces binaries much like 7 | C++, Go, and Rust do. Now with GitHub Actions the cross-platform build and 8 | release process can be automated for free for Open Source projects. This is an 9 | enormous opportunity to make the Python ecosystem 20-50% faster with a single 10 | pull request. This lightning talk walks through a GitHub workflow that 11 | publishes Cython-optimized wheels to PyPI. Discover how Cython can turbo-charge 12 | your Python code and GitHub Actions can simplify your cross-platform release 13 | process for free. 14 | 15 | 16 | SF Python Holiday Party 2019 17 | ---------------------------- 18 | 19 | | Grant Jenks 20 | | Dec. 4, 2019 21 | | `Cython for All with GitHub Actions Video`_ 22 | | `grantjenks.com/docs/cython-for-all`_ 23 | | `github.com/grantjenks/python-c2f`_ 24 | | `pypi.org/project/c2f/#files`_ 25 | 26 | 27 | Is Python interpreted or compiled? 28 | ---------------------------------- 29 | 30 | | . 31 | | . 32 | | . 33 | | ? 34 | | . 35 | | . 36 | | . 37 | | ? 38 | | . 39 | | . 40 | | . 41 | | ? 42 | 43 | 44 | c2f.py 45 | ------ 46 | 47 | .. code-block:: python 48 | 49 | "Celsius to Fahrenheit Library" 50 | 51 | def convert(celsius: float) -> float: 52 | "Convert Celsius to Fahrenheit" 53 | fahrenheit = celsius * 1.8 + 32 54 | return fahrenheit 55 | 56 | 57 | c2f.cpython-38.pyc 58 | ------------------ 59 | 60 | .. code-block:: pycon 61 | 62 | >>> import c2f 63 | >>> dis.dis(c2f.convert) 64 | 6 0 LOAD_FAST 0 (celsius) 65 | 2 LOAD_CONST 1 (1.8) 66 | 4 BINARY_MULTIPLY 67 | 6 LOAD_CONST 2 (32) 68 | 8 BINARY_ADD 69 | 10 STORE_FAST 1 (fahrenheit) 70 | 7 12 LOAD_FAST 1 (fahrenheit) 71 | 14 RETURN_VALUE 72 | 73 | 74 | setup.py 75 | -------- 76 | 77 | .. code-block:: python 78 | 79 | from setuptools import setup 80 | from Cython.Build import cythonize 81 | 82 | setup( 83 | name='c2f', 84 | version='0.0.0', 85 | py_modules=['c2f'], 86 | ext_modules=cythonize('c2f.py'), 87 | ) 88 | 89 | 90 | c2f.c 91 | ----- 92 | 93 | .. code-block:: shell 94 | 95 | $ cython c2f.py 96 | 97 | .. code-block:: c 98 | 99 | static PyObject * __pyx_convert(double __pyx_v_celsius) 100 | { 101 | double __pyx_v_fahrenheit; 102 | PyObject *__pyx_r = NULL; 103 | __pyx_v_fahrenheit = ((__pyx_v_celsius * 1.8) + 32.0); 104 | __pyx_r = PyFloat_FromDouble(__pyx_v_fahrenheit); 105 | return __pyx_r; 106 | } 107 | 108 | 109 | c2f.so 110 | ------ 111 | 112 | .. code-block:: shell 113 | 114 | $ python setup.py bdist_wheel 115 | 116 | .. code-block:: nasm 117 | 118 | ___pyx_convert: 119 | push rbp 120 | mov rbp, rsp 121 | sub rsp, 16 122 | movsd xmm0, qword ptr [rbp - 8] 123 | mulsd xmm0, qword ptr [rip + 1379] 124 | addsd xmm0, qword ptr [rip + 1379] 125 | call 502 126 | add rsp, 16 127 | pop rbp 128 | ret 129 | 130 | 131 | .github/workflows/release.yml 132 | ----------------------------- 133 | 134 | .. code-block:: yaml 135 | 136 | name: release 137 | on: 138 | push: 139 | tags: 140 | - v* 141 | jobs: 142 | build-linux-cp38: 143 | runs-on: ubuntu-latest 144 | container: quay.io/pypa/manylinux2014_x86_64 145 | steps: 146 | ... 147 | 148 | 149 | Matrix Build 150 | ------------ 151 | 152 | .. code-block:: yaml 153 | 154 | build-macos: 155 | runs-on: macos-latest 156 | strategy: 157 | max-parallel: 4 158 | matrix: 159 | python-version: [3.5, 3.6, 3.7, 3.8] 160 | steps: 161 | ... 162 | 163 | 164 | Mac Build Steps 165 | --------------- 166 | 167 | .. code-block:: yaml 168 | 169 | - name: Set up Python ${{ matrix.python-version }} x64 170 | uses: actions/setup-python@v1 171 | with: 172 | python-version: ${{ matrix.python-version }} 173 | architecture: x64 174 | 175 | - name: Install package dependencies 176 | run: pip install cython wheel 177 | 178 | - name: Build binary wheel 179 | run: python setup.py bdist_wheel 180 | 181 | 182 | Linux auditwheel Tool 183 | --------------------- 184 | 185 | .. code-block:: yaml 186 | 187 | - name: Build binary wheel 188 | run: /opt/python/cp38-cp38/bin/python setup.py bdist_wheel 189 | 190 | - name: Apply auditwheel for manylinux wheel 191 | run: auditwheel repair -w dist dist/* 192 | 193 | - name: Remove linux wheel 194 | run: rm dist/*-linux_x86_64.whl 195 | 196 | 197 | Windows Build Steps 198 | ------------------- 199 | 200 | .. code-block:: yaml 201 | 202 | - name: Download Build Tools for Visual Studio 2019 203 | run: Invoke-WebRequest -Uri https://aka.ms/vs/16/rel... 204 | 205 | - name: Run vs_buildtools.exe install 206 | run: ./vs_buildtools.exe --quiet --wait --norestart ... 207 | 208 | 209 | Store Build Artifacts 210 | --------------------- 211 | 212 | .. code-block:: yaml 213 | 214 | - name: Archive dist artifacts 215 | uses: actions/upload-artifact@v1 216 | with: 217 | name: dist-macos-${{ matrix.python-version }} 218 | path: dist 219 | 220 | 221 | Source Distribution 222 | ------------------- 223 | 224 | .. code-block:: yaml 225 | 226 | upload: 227 | needs: [build-linux-cp35, ...] 228 | runs-on: ubuntu-latest 229 | steps: 230 | ... 231 | - name: Install dependencies 232 | run: pip install -r requirements.txt 233 | 234 | - name: Create source dist 235 | run: python setup.py sdist 236 | 237 | 238 | Stage Binary Wheels 239 | ------------------- 240 | 241 | .. code-block:: yaml 242 | 243 | - name: Stage linux 3.8 244 | uses: actions/download-artifact@v1 245 | with: 246 | name: dist-linux-3.8 247 | - run: mv -v dist-linux-3.8/* dist/ 248 | 249 | - name: Stage macos 3.8 250 | uses: actions/download-artifact@v1 251 | with: 252 | name: dist-macos-3.8 253 | - run: mv -v dist-macos-3.8/* dist/ 254 | ... 255 | 256 | 257 | Upload with Twine 258 | ----------------- 259 | 260 | .. code-block:: yaml 261 | 262 | - name: Upload with twine 263 | env: 264 | TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }} 265 | TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }} 266 | run: | 267 | ls -l dist/* 268 | pip install twine 269 | twine upload dist/* 270 | 271 | 272 | Cythonize all the Things! 273 | ------------------------- 274 | 275 | PLEASE STEAL THE CODE! 276 | 277 | | `grantjenks.com/docs/cython-for-all`_ 278 | | `github.com/grantjenks/python-c2f`_ 279 | | `pypi.org/project/c2f/#files`_ 280 | 281 | *Cythonize all the Things!* 282 | 283 | *Cythonize all the Things!* 284 | 285 | *Cythonize all the Things!* 286 | 287 | *Cythonize all the Things!* 288 | 289 | *Cythonize all the Things!* 290 | 291 | *Cythonize all the Things!* 292 | 293 | .. _`Cython for All with GitHub Actions Video`: https://www.youtube.com/watch?v=-7_07O5ENhU 294 | .. _grantjenks.com/docs/cython-for-all: http://grantjenks.com/docs/cython-for-all/ 295 | .. _github.com/grantjenks/python-c2f: https://github.com/grantjenks/python-c2f/ 296 | .. _pypi.org/project/c2f/#files: https://pypi.org/project/c2f/#files 297 | 298 | 299 | Appendix 300 | -------- 301 | 302 | Dumping Assembly 303 | ................ 304 | 305 | .. code-block:: shell 306 | 307 | $ gcc -g -shared -pthread -fPIC -fwrapv -O2 -Wall -fno-strict-aliasing -I/Library/Frameworks/Python.framework/Versions/3.8/include/python3.8 -L/Library/Frameworks/Python.framework/Versions/3.8/lib -o c2f.so c2f.c -lpython3.8 308 | $ objdump -S -df=___pyx_pw_3c2f_1convert c2f.so 309 | 310 | 311 | Git Tagging 312 | ........... 313 | 314 | .. code-block:: shell 315 | 316 | $ git tag -a v0.0.2 -m v0.0.2 317 | $ git push 318 | $ git push --tags 319 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | Cython for All with GitHub Actions 2 | ================================== 3 | 4 | .. include:: toc.rst 5 | 6 | Is Python an interpreted or compiled language? Trick question. It’s actually 7 | both. With tools like Cython, we can take the compilation step further and 8 | remove the interpreter loop almost entirely. Cython produces binaries much like 9 | C++, Go, and Rust do. Now with GitHub Actions the cross-platform build and 10 | release process can be automated for free for Open Source projects. This is an 11 | enormous opportunity to make the Python ecosystem 20-50% faster with a single 12 | pull request. This lightning talk walks through a GitHub workflow that 13 | publishes Cython-optimized wheels to PyPI. Discover how Cython can turbo-charge 14 | your Python code and GitHub Actions can simplify your cross-platform release 15 | process for free. 16 | 17 | 18 | SF Python Holiday Party 2019 19 | ---------------------------- 20 | 21 | | Grant Jenks 22 | | Dec. 4, 2019 23 | | `grantjenks.com/docs/cython-for-all`_ 24 | | `github.com/grantjenks/python-c2f`_ 25 | | `pypi.org/project/c2f/#files`_ 26 | 27 | 28 | Is Python interpreted or compiled? 29 | ---------------------------------- 30 | 31 | | . 32 | | . 33 | | . 34 | | ? 35 | | . 36 | | . 37 | | . 38 | | ? 39 | | . 40 | | . 41 | | . 42 | | ? 43 | 44 | 45 | c2f.py 46 | ------ 47 | 48 | .. code-block:: python 49 | :linenos: 50 | :emphasize-lines: 5 51 | 52 | "Celsius to Fahrenheit Library" 53 | 54 | def convert(celsius: float) -> float: 55 | "Convert Celsius to Fahrenheit" 56 | fahrenheit = celsius * 1.8 + 32 57 | return fahrenheit 58 | 59 | 60 | c2f.cpython-38.pyc 61 | ------------------ 62 | 63 | .. code-block:: pycon 64 | :linenos: 65 | :emphasize-lines: 2 66 | 67 | >>> import c2f 68 | >>> dis.dis(c2f.convert) 69 | 6 0 LOAD_FAST 0 (celsius) 70 | 2 LOAD_CONST 1 (1.8) 71 | 4 BINARY_MULTIPLY 72 | 6 LOAD_CONST 2 (32) 73 | 8 BINARY_ADD 74 | 10 STORE_FAST 1 (fahrenheit) 75 | 7 12 LOAD_FAST 1 (fahrenheit) 76 | 14 RETURN_VALUE 77 | 78 | 79 | setup.py 80 | -------- 81 | 82 | .. code-block:: python 83 | :linenos: 84 | :emphasize-lines: 2, 8 85 | 86 | from setuptools import setup 87 | from Cython.Build import cythonize 88 | 89 | setup( 90 | name='c2f', 91 | version='0.0.0', 92 | py_modules=['c2f'], 93 | ext_modules=cythonize('c2f.py'), 94 | ) 95 | 96 | 97 | c2f.c 98 | ----- 99 | 100 | .. code-block:: shell 101 | 102 | $ cython c2f.py 103 | 104 | .. code-block:: c 105 | :linenos: 106 | :emphasize-lines: 5 107 | 108 | static PyObject * __pyx_convert(double __pyx_v_celsius) 109 | { 110 | double __pyx_v_fahrenheit; 111 | PyObject *__pyx_r = NULL; 112 | __pyx_v_fahrenheit = ((__pyx_v_celsius * 1.8) + 32.0); 113 | __pyx_r = PyFloat_FromDouble(__pyx_v_fahrenheit); 114 | return __pyx_r; 115 | } 116 | 117 | 118 | c2f.so 119 | ------ 120 | 121 | .. code-block:: shell 122 | 123 | $ python setup.py bdist_wheel 124 | 125 | .. code-block:: nasm 126 | :linenos: 127 | :emphasize-lines: 6, 7 128 | 129 | ___pyx_convert: 130 | push rbp 131 | mov rbp, rsp 132 | sub rsp, 16 133 | movsd xmm0, qword ptr [rbp - 8] 134 | mulsd xmm0, qword ptr [rip + 1379] 135 | addsd xmm0, qword ptr [rip + 1379] 136 | call 502 137 | add rsp, 16 138 | pop rbp 139 | ret 140 | 141 | 142 | .github/workflows/release.yml 143 | ----------------------------- 144 | 145 | .. code-block:: yaml 146 | :linenos: 147 | :emphasize-lines: 5, 9 148 | 149 | name: release 150 | on: 151 | push: 152 | tags: 153 | - v* 154 | jobs: 155 | build-linux-cp38: 156 | runs-on: ubuntu-latest 157 | container: quay.io/pypa/manylinux2014_x86_64 158 | steps: 159 | ... 160 | 161 | 162 | Matrix Build 163 | ------------ 164 | 165 | .. code-block:: yaml 166 | :linenos: 167 | :emphasize-lines: 4, 5, 6 168 | 169 | build-macos: 170 | runs-on: macos-latest 171 | strategy: 172 | max-parallel: 4 173 | matrix: 174 | python-version: [3.5, 3.6, 3.7, 3.8] 175 | steps: 176 | ... 177 | 178 | 179 | Mac Build Steps 180 | --------------- 181 | 182 | .. code-block:: yaml 183 | :linenos: 184 | :emphasize-lines: 1, 7, 10 185 | 186 | - name: Set up Python ${{ matrix.python-version }} x64 187 | uses: actions/setup-python@v1 188 | with: 189 | python-version: ${{ matrix.python-version }} 190 | architecture: x64 191 | 192 | - name: Install package dependencies 193 | run: pip install cython wheel 194 | 195 | - name: Build binary wheel 196 | run: python setup.py bdist_wheel 197 | 198 | 199 | Linux auditwheel Tool 200 | --------------------- 201 | 202 | .. code-block:: yaml 203 | :linenos: 204 | :emphasize-lines: 1, 4, 7 205 | 206 | - name: Build binary wheel 207 | run: /opt/python/cp38-cp38/bin/python setup.py bdist_wheel 208 | 209 | - name: Apply auditwheel for manylinux wheel 210 | run: auditwheel repair -w dist dist/* 211 | 212 | - name: Remove linux wheel 213 | run: rm dist/*-linux_x86_64.whl 214 | 215 | 216 | Windows Build Steps 217 | ------------------- 218 | 219 | .. code-block:: yaml 220 | :linenos: 221 | :emphasize-lines: 1, 4 222 | 223 | - name: Download Build Tools for Visual Studio 2019 224 | run: Invoke-WebRequest -Uri https://aka.ms/vs/16/rel... 225 | 226 | - name: Run vs_buildtools.exe install 227 | run: ./vs_buildtools.exe --quiet --wait --norestart ... 228 | 229 | 230 | Store Build Artifacts 231 | --------------------- 232 | 233 | .. code-block:: yaml 234 | :linenos: 235 | :emphasize-lines: 2 236 | 237 | - name: Archive dist artifacts 238 | uses: actions/upload-artifact@v1 239 | with: 240 | name: dist-macos-${{ matrix.python-version }} 241 | path: dist 242 | 243 | 244 | Source Distribution 245 | ------------------- 246 | 247 | .. code-block:: yaml 248 | :linenos: 249 | :emphasize-lines: 2, 6, 9 250 | 251 | upload: 252 | needs: [build-linux-cp35, ...] 253 | runs-on: ubuntu-latest 254 | steps: 255 | ... 256 | - name: Install dependencies 257 | run: pip install -r requirements.txt 258 | 259 | - name: Create source dist 260 | run: python setup.py sdist 261 | 262 | 263 | Stage Binary Wheels 264 | ------------------- 265 | 266 | .. code-block:: yaml 267 | :linenos: 268 | :emphasize-lines: 2, 4, 5 269 | 270 | - name: Stage linux 3.8 271 | uses: actions/download-artifact@v1 272 | with: 273 | name: dist-linux-3.8 274 | - run: mv -v dist-linux-3.8/* dist/ 275 | 276 | - name: Stage macos 3.8 277 | uses: actions/download-artifact@v1 278 | with: 279 | name: dist-macos-3.8 280 | - run: mv -v dist-macos-3.8/* dist/ 281 | ... 282 | 283 | 284 | Upload with Twine 285 | ----------------- 286 | 287 | .. code-block:: yaml 288 | :linenos: 289 | :emphasize-lines: 3, 4, 8 290 | 291 | - name: Upload with twine 292 | env: 293 | TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }} 294 | TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }} 295 | run: | 296 | ls -l dist/* 297 | pip install twine 298 | twine upload dist/* 299 | 300 | 301 | Cythonize all the Things! 302 | ------------------------- 303 | 304 | PLEASE STEAL THE CODE! 305 | 306 | | `grantjenks.com/docs/cython-for-all`_ 307 | | `github.com/grantjenks/python-c2f`_ 308 | | `pypi.org/project/c2f/#files`_ 309 | 310 | *Cythonize all the Things!* 311 | 312 | *Cythonize all the Things!* 313 | 314 | *Cythonize all the Things!* 315 | 316 | *Cythonize all the Things!* 317 | 318 | *Cythonize all the Things!* 319 | 320 | *Cythonize all the Things!* 321 | 322 | .. _grantjenks.com/docs/cython-for-all: http://grantjenks.com/docs/cython-for-all/ 323 | .. _github.com/grantjenks/python-c2f: https://github.com/grantjenks/python-c2f/ 324 | .. _pypi.org/project/c2f/#files: https://pypi.org/project/c2f/#files 325 | 326 | 327 | Appendix 328 | -------- 329 | 330 | Dumping Assembly 331 | ................ 332 | 333 | .. code-block:: shell 334 | 335 | $ gcc -g -shared -pthread -fPIC -fwrapv -O2 -Wall -fno-strict-aliasing -I/Library/Frameworks/Python.framework/Versions/3.8/include/python3.8 -L/Library/Frameworks/Python.framework/Versions/3.8/lib -o c2f.so c2f.c -lpython3.8 336 | $ objdump -S -df=___pyx_pw_3c2f_1convert c2f.so 337 | 338 | 339 | Git Tagging 340 | ........... 341 | 342 | .. code-block:: shell 343 | 344 | $ git tag -a v0.0.2 -m v0.0.2 345 | $ git push 346 | $ git push --tags 347 | --------------------------------------------------------------------------------