{"id":35876831,"url":"https://github.com/hajimen/cef-capi-py","last_synced_at":"2026-01-08T17:02:48.890Z","repository":{"id":272855817,"uuid":"917967062","full_name":"hajimen/cef-capi-py","owner":"hajimen","description":"cef-capi-py is Chromium Embedded Framework (CEF) C API wrapper for Python without C extension, with ctypes.","archived":false,"fork":false,"pushed_at":"2025-06-17T10:24:56.000Z","size":755,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-30T07:25:59.389Z","etag":null,"topics":["cef","chromium-embedded-framework","ctypes","python"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hajimen.png","metadata":{"files":{"readme":"ReadMe.md","changelog":"ChangeLog.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-01-17T01:33:24.000Z","updated_at":"2025-06-17T10:25:00.000Z","dependencies_parsed_at":"2025-01-17T02:58:56.515Z","dependency_job_id":"f60d36b0-11a5-47b2-92f4-759762dbaf35","html_url":"https://github.com/hajimen/cef-capi-py","commit_stats":null,"previous_names":["hajimen/cef-capi-py"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/hajimen/cef-capi-py","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hajimen%2Fcef-capi-py","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hajimen%2Fcef-capi-py/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hajimen%2Fcef-capi-py/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hajimen%2Fcef-capi-py/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hajimen","download_url":"https://codeload.github.com/hajimen/cef-capi-py/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hajimen%2Fcef-capi-py/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28246996,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2026-01-08T02:00:06.591Z","response_time":241,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cef","chromium-embedded-framework","ctypes","python"],"created_at":"2026-01-08T17:00:42.751Z","updated_at":"2026-01-08T17:02:48.885Z","avatar_url":"https://github.com/hajimen.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# cef-capi-py\n\n**cef-capi-py** is Chromium Embedded Framework (CEF) C API wrapper for Python without C extension, with ctypes.\n\ncef-capi-py wheel contains CEF runtime, of course.\n\n## Introduction\n\nI need newer Python but I don't need newer CEF!\n\nPython C extension is a good thing but it has a severe drawback: ABI instability.\nAlmost everyone ignores [Limited API](https://docs.python.org/3/c-api/stable.html#limited-c-api),\ne.g. [pybind11](https://github.com/pybind/pybind11).\nYes, [cefpython](https://github.com/cztomczak/cefpython) ignores too.\nSo we have to build C extension every year, 3.7, 3.8, 3.9, 3.10...\nI deeply understand why cefpython has stopped to update.\n\nI need newer Python but I don't need newer CEF,\nbecause I just want to embed a local web app in my Python app.\nThe local web app never requires newer CEF.\n\n**cef-capi-py** is the resolution. It does not use C extension. So we don't have to build C extension every year anymore.\nInstead of C extension, cef-capi-py uses [ctypes](https://docs.python.org/3/library/ctypes.html).\n\nWithout type hints, using CEF is a big pain. I have tried PyObjC for Mac GUI, and abandoned for the reason\n(this is why cef-capi-py lacks Chromium-like window app example for Mac).\nNo problem, [ctypeslib](https://github.com/trolldbois/ctypeslib) and mypy does great jobs for cef-capi-py.\nThey are not perfect, but good enough.\n\n## Design policy\n\nI have hacked cefpython and [made it work with Python 3.12 for Windows x86-64](https://github.com/hajimen/cefpython).\nI feel it is not maintainable. cefpython's codebase and documents are too big, and I need just a part of it.\nFor example, I don't need Python 2.7 support.\n\nThe primary target of cef-capi-py is maintainability. It means minimal codebase and document.\nEvery non-essential thing is abandoned.\n\n- No document of CEF: See original CEF documents. cef-capi-py is a thin wrapper of [CEF C API](https://github.com/chromiumembedded/cef/tree/master/include/capi). You should get **userfree** idea of CEF from CEF documents, and keep it in your code, for example.\n- Little document of cef-capi-py itself: This ReadMe.md is almost everything. Learn with touching the example codes.\n- No comprehensive test: Just a simple smoke test for safeguard. Run `python -m cef_capi.smoke_test`.\n- No automated build: In Python, everything changes rapidly. CEF also changes rapidly. Automated build requires much cost to maintain.\n- No frequent update: I will not update cef-capi-py while it works. I predict that someday OSes can block old CEF, someday Python can break some ctypes features. Except such cases, I will not update cef-capi-py.\n- No optimization: Optimization makes codebase unmaintainable.\n- Single process mode for V8 extension (JavaScript interaction): CEF usually creates **render process** as subprocess. V8 extension code should be written in render process, but it is extremely hard for Python. I just go with single process mode.\n- On macOS, Homebrew python is required. I have failed to manage to do with python.org pkg python.\n\n## Set up to learn / build\n\nCEF is a gigantic codebase, so it is not very kind to newbies.\ncef-capi-py is quite small except generated codes, but it has little document.\nLearn from examples. We are going to set up for touching the examples.\n\n### All platforms\n\nFirst of all, `git clone` this repository, and set up venv or something.\n\nRun `pip install -r requirements.txt` in repo root.\n\nYou should grab the corresponding version of CEF tar.bz2 file from [CEF Automated Builds](https://cef-builds.spotifycdn.com/index.html).\nEnter the version number of the cef-capi-py (131.3.5, for example) into \"Version Filer\" and press \"Apply\" button.\nYou will find them. Download **Sample Application** and **Minimal Distribution** tar.bz2 files and extract them\nto `cef_binary/client` and `cef_binary/minimal`.\nIf you are just going to learn the behavior of cef-capi-py, leave `cef_binary/minimal` untouched.\nSee the `ReadMe.md` files of each directory.\n\nIn Windows, `cef_binary/client/Release/cefclient.exe` should be found.\n\n### Linux\n\nCEF depends on libgtk-3, libnss3, and libasound2.\nFor Ubuntu, `sudo apt install libgtk-3-dev libnss3 libasound2t64`.\nEven just running windowless, CEF requires X11.\nIn the case, `sudo apt install xorg xvfb x11-xkb-utils`.\n\nIf you don't have dbus, CEF reports errors, but you can ignore them.\n\n### Linux aarch64 (ARM64)\n\nRun `export LD_PRELOAD=/{somewhere}/libcef.so` before running CEF.\nIt comes from a bug of CEF Automated Builds: [Dynamic loading of libcef.so on Linux ARM64 not possible anymore due to TLS size increase](https://github.com/chromiumembedded/cef/issues/3803).\n\n### macOS\n\nUse Homebrew's python.\n\nRun `xattr -cr cef_binary` in repo root. Ignore permission error.\n\n### Shell\n\n- Linux and macOS: bash\n- Windows: PowerShell\n\n## Running examples\n\n### Chromium-like window app\n\nYou should see a Chromium-like window app with:\n\n- Windows: `python -m examples.window_win`.\n- Linux: `python -m examples.window_linux`. **CAUTION**: In Linux, you need to hit a key of your keyboard before closing the app window.\nWithout hitting a key, the app does not exit. Maybe a bug somewhere.\n- Mac: I don't have it yet.\n\n### Screenshot\n\nYou should have `screenshot.png` with `python -m examples.screenshot`.\n\n### V8 extension (JavaScript interaction)\n\nRun `python -m examples.javascript` and look at the stdout of it.\nYou should see `Foo() called with right arg \"x from javascript.py:execute_bar()\".` in it.\n\n## Commentary of odd things\n\n### `cef_capi.{platform tag}.header` and `cef_capi.{platform tag}.struct`\n\n`header.py` is generated by ctypeslib. But it lacks C struct field name hint when we use it with VSCode.\n`struct.py` can do hinting. But `struct.py` cannot find function declaration with F12 in VSCode.\nFor CEF functions, use `cef_capi.{platform tag}.header`. For CEF structs, use `cef_capi.{platform tag}.struct`.\n\n`cef_capi.header` and `cef_capi.struct` are for platform independent code.\n\n### Decorators `@handler()` and `@task_factory`\n\nLook at `examples/screenshot.py`.\n\nCEF C API uses C-style OOP. `@handler()` decorator is for member function overriding.\n`@handler()` gets the decorated function's name and uses the name as member function's name.\nVery odd usage but good for simplicity.\n\n`@handler()` decorator has four odd feat.ures. Look at `cef_capi/__init__.py` until\nyou get the meaning of them:\n\n- **Auto dereferencing** of `ctypes._Pointer` instances of args\n- kwarg `raw_arg_indices: set[int]` to disable auto dereferencing.\n- kwarg `ignore_arg_indices: set[int]` to ignore namely `self` arg. If you need `self` for handler arg, write `ignore_arg_indices=set()` in `@handler()` arg.\n- If the return value is `cef_base_ref_counted_t`-ed struct instance or its pointer, it is converted to `int`. (see `cef_pointer_to_struct()` subsection below)\n\n`@task_factory` decorator converts the decorated function to `cef_task_t` ctor.\nCEF deletes `cef_task_t` instance after `execute()` call.\nYou have to construct `cef_task_t` every task post.\nThe ctor can pass args to `execute()`.\n\n### `cef_pointer_to_struct()`\n\nLook at `examples/javascript.py`.\n\nIt's a long story; It comes from 15-year bug of Python ctypes: [Python ctypes callback function gives \"TypeError: invalid result type for callback function\"](https://stackoverflow.com/questions/33005127/python-ctypes-callback-function-gives-typeerror-invalid-result-type-for-callba).\n\nCEF C API uses C-style OOP. For the bug, the member function cannot return a pointer to struct.\nSo we have to use `int` instead of `ctypes._Pointer` in the case.\n\n`cef_pointer_to_struct()` converts the `int` to `ctypes.Structure`.\n\n### `import faulthandler; faulthandler.enable()`\n\nCEF easily raises segmentation fault. Python does not show any error message for segmentation fault without\n`import faulthandler; faulthandler.enable()`.\n\n### CEF can hang up\n\nSometimes CEF hangs up. You should have a timeout / retry mechanism in your products.\nSee the example of timeout / retry mechanism in `cef_capi/smoke_test.py`.\n\n### Error reports from CEF\n\nYou can ignore them for most usage.\n`settings.log_severity = struct.LOGSEVERITY_DISABLE` stops most CEF error reporting. Not all.\n\n### Memory management?\n\nNote the possibility of memory leaks. Python and CEF memory management style mismatch is too notorious for us.\n\n## Integrating to your product\n\nUse wheel. It contains CEF runtime, of course. You can find it in [PyPI](https://pypi.org/project/cef-capi-py/).\n\nThe version of wheel is CEF runtime's.\n\n## Generating ctypes thunk and type hint stub\n\nI don't want to update cef-capi-py frequently, but sooner or later the day will come.\nThis section is for the day.\n\nCEF changes rapidly, including its API header files. Once API header files have been changed,\nwe need to re-generate ctypes thunk (`cef_capi/*/header.py`) and its type hint stub (`cef_capi/*/struct.pyi`).\n\n### All platforms\n\nctypes thunk generation depends on `ctypeslib2==2.3.4`. Before generation, we need to fix the bugs of it.\n\n- Windows: ```python -m patch -d (python -c 'import sysconfig; print(sysconfig.get_path(\"purelib\"))') tool/ctypeslib.patch```\n- Others (bash): ```python -m patch -d `python -c 'import sysconfig; print(sysconfig.get_path(\"purelib\"))'` tool/ctypeslib.patch```\n\n### Windows\n\nInstall LLVM 15. [LLVM Download Page](https://releases.llvm.org/download.html)\n\nThe ctypeslib2 command is:\n\n```powershell\n# start from repo root\ncd cef_binary/minimal\nclang2py -c -o ../../cef_capi/win_amd64/header.py -i -k cdefstu (Get-Item include/capi/*.h) include/cef_version.h -r cef.* -r CEF.* --clang-args=\"-I.\"\n```\n\nGenerated `cef_capi/win_amd64/header.py` should regex replace: `CFUNCTYPE\\(ctypes\\.POINTER\\(\\w*\\)` to `CFUNCTYPE(ctypes.c_uint64` for 15-year bug of Python ctypes: [Python ctypes callback function gives \"TypeError: invalid result type for callback function\"](https://stackoverflow.com/questions/33005127/python-ctypes-callback-function-gives-typeerror-invalid-result-type-for-callba)\n\nIn `cef_capi/win_amd64/header.py`, replace:\n\n```python\n_libraries['FIXME_STUB'] = FunctionFactoryStub() #  ctypes.CDLL('FIXME_STUB')\n```\n\nto\n\n```python\nfrom cef_capi import LIBCEF_PATH\n_libraries['FIXME_STUB'] = ctypes.WinDLL(str(LIBCEF_PATH))\n```\n\nAnd mypy command:\n\n```powershell\n# start from repo root\nstubgen -m cef_capi.win_amd64.header -o . --inspect-mode\nRemove-Item ./cef_capi/win_amd64/struct.pyi\nRename-Item cef_capi/win_amd64/header.pyi struct.pyi\n```\n\n### Linux\n\nclang-15 is required. For Ubuntu, `sudo apt install clang-15`.\n\nThe ctypeslib2 command is:\n\n```bash\n# start from repo root\ncd cef_binary/minimal\nclang2py -c -o ../../cef_capi/linux_`uname -m`/header.py -i -k cdefstu include/capi/*.h include/cef_version.h -r cef.* -r CEF.* --clang-args=\"-I.\"\nclang2py -c -o ../../examples/linux_`uname -m`/gtk_x_header.py -i -k cdefstu /usr/include/X11/Xlib.h /usr/include/gtk-3.0/gtk/gtk.h /usr/include/gtk-3.0/gdk/gdkx.h -r \"^X.*\" -r \"^gtk.*\" -r \"^Gtk.*\" -r \"^g_.*\" -r \"^G_.*\" -r \"^gdk_.*\" -r \"^Gdk.*\" --clang-args=\"-I. -I/usr/include/gtk-3.0 -I/usr/include/glib-2.0 -I/usr/lib/`uname -m`-linux-gnu/glib-2.0/include -I/usr/include/pango-1.0 -I/usr/include/harfbuzz -I/usr/include/cairo -I/usr/include/gdk-pixbuf-2.0 -I/usr/include/atk-1.0\" -l gtk-3\n```\n\nGenerated `cef_capi/linux_{machine hardware name}/header.py` and `examples/linux_{machine hardware name}/gtk_x_header.py` should be regex replaced: `CFUNCTYPE\\(ctypes\\.POINTER\\(\\w*\\)` to `CFUNCTYPE(ctypes.c_uint64` for 15-year bug of Python ctypes: [Python ctypes callback function gives \"TypeError: invalid result type for callback function\"](https://stackoverflow.com/questions/33005127/python-ctypes-callback-function-gives-typeerror-invalid-result-type-for-callba)\n\nIn `cef_capi/linux_{machine hardware name}/header.py`, replace:\n\n```python\n_libraries['FIXME_STUB'] = FunctionFactoryStub() #  ctypes.CDLL('FIXME_STUB')\n```\n\nto\n\n```python\nfrom cef_capi import LIBCEF_PATH\n_libraries['FIXME_STUB'] = ctypes.CDLL(str(LIBCEF_PATH))\n```\n\nIn `examples/linux_{machine hardware name}/gtk_x_header.py`, add after `_libraries = {}` and `FunctionFactoryStub` class def both:\n\n```python\nimport ctypes.util\n_libraries['FIXME_STUB'] = FunctionFactoryStub() #  ctypes.CDLL('FIXME_STUB')\n_libraries['libgtk-3.so.0'] = ctypes.CDLL(ctypes.util.find_library('gtk-3'))\n```\n\nAnd mypy command:\n\n```bash\n# start from repo root\nstubgen -m cef_capi.linux_`uname -m`.header -o . --inspect-mode\nrm cef_capi/linux_`uname -m`/struct.pyi\nmv cef_capi/linux_`uname -m`/header.pyi cef_capi/linux_`uname -m`/struct.pyi\n```\n\n### macOS\n\nAssuming Apple Silicon.\n\nclang-15 is required. Set up Homebrew and `brew install llvm@15`. If you are going to generate x86_64 too,\n`arch -x86_64 /opt/homebrew-x86_64/bin/brew install llvm@15`. Each arch requires different `cef_binary`.\n\nThe ctypeslib2 command is:\n\n```bash\n# start from repo root\nexport MHN=\"arm64\" # or x86_64\nsource tool/macosx-clang-15-${MHN}.sh\ncd cef_binary/minimal\nclang2py -c -o ../../cef_capi/macosx_${MHN}/header.py -i -k cdefstu include/capi/*.h include/cef_version.h -r cef.* -r CEF.* --clang-args=\"-I.\"\n```\n\nGenerated `cef_capi/macosx_{machine hardware name}/header.py` should regex replace: `CFUNCTYPE\\(ctypes\\.POINTER\\(\\w*\\)` to `CFUNCTYPE(ctypes.c_uint64` for 15-year bug of Python ctypes: [Python ctypes callback function gives \"TypeError: invalid result type for callback function\"](https://stackoverflow.com/questions/33005127/python-ctypes-callback-function-gives-typeerror-invalid-result-type-for-callba)\n\nIn `cef_capi/macosx_{machine hardware name}/header.py`, add after `_libraries = {}` and `FunctionFactoryStub` class def both:\n\n```python\nfrom cef_capi import LIBCEF_PATH\n_libraries['FIXME_STUB'] = ctypes.CDLL(str(LIBCEF_PATH))\n```\n\nAnd mypy command:\n\n```bash\n# start from repo root\nexport MHN=\"arm64\" # or x86_64\nstubgen -m cef_capi.macosx_${MHN}.header -o . --inspect-mode\nrm cef_capi/macosx_${MHN}/struct.pyi\nmv cef_capi/macosx_${MHN}/header.pyi cef_capi/macosx_${MHN}/struct.pyi\n```\n\n### Any platform\n\nGenerate version info of CEF in any platform:\n\n```\n# start from repo root\ncd cef_binary/minimal\nclang2py -c -o ../../cef_capi/version.py -x -k m include/cef_version.h -r cef.* -r CEF.* --clang-args=\"-I.\"\n```\n\n## Build wheel\n\nSet up `cef_binary/client` as above. Then:\n\n- Windows: `python -m build --wheel --config-setting=--build-option=--plat-name=win_amd64`\n- Linux:\n\n```bash\n# start from repo root\nstrip cef_binary/client/Release/*.so cef_binary/client/Release/cefsimple\npython -m build --wheel --config-setting=--build-option=--plat-name=manylinux2014_`uname -m`\n```\n- macOS:\n\n```bash\n# start from repo root\nexport MHN=\"arm64\" # or x86_64\npython -m build --wheel --config-setting=--build-option=--plat-name=macosx_11_0_${MHN}\n```\n\nDo not forget to check the wheel by `python -m cef_capi.smoke_test`.\n\n## License\n\nMIT License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhajimen%2Fcef-capi-py","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhajimen%2Fcef-capi-py","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhajimen%2Fcef-capi-py/lists"}