JetBrains / lets-plot

Multiplatform plotting library based on the Grammar of Graphics
https://lets-plot.org
MIT License
1.56k stars 49 forks source link

Mac M3 Python can't find Cairo #1090

Open hathawayj opened 5 months ago

hathawayj commented 5 months ago
ggsave(chart, "anychart.png")

results in

{
    "name": "OSError",
    "message": "no library called \"cairo-2\" was found\nno library called \"cairo\" was found\nno library called \"libcairo-2\" was found\ncannot load library 'libcairo.so.2': dlopen(libcairo.so.2, 0x0002): tried: 'libcairo.so.2' (no such file), '/System/Volumes/Preboot/Cryptexes/OSlibcairo.so.2' (no such file), '/usr/lib/libcairo.so.2' (no such file, not in dyld cache), 'libcairo.so.2' (no such file), '/usr/lib/libcairo.so.2' (no such file, not in dyld cache).  Additionally, ctypes.util.find_library() did not manage to locate a library called 'libcairo.so.2'\ncannot load library 'libcairo.2.dylib': dlopen(libcairo.2.dylib, 0x0002): tried: 'libcairo.2.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OSlibcairo.2.dylib' (no such file), '/usr/lib/libcairo.2.dylib' (no such file, not in dyld cache), 'libcairo.2.dylib' (no such file), '/usr/lib/libcairo.2.dylib' (no such file, not in dyld cache).  Additionally, ctypes.util.find_library() did not manage to locate a library called 'libcairo.2.dylib'\ncannot load library 'libcairo-2.dll': dlopen(libcairo-2.dll, 0x0002): tried: 'libcairo-2.dll' (no such file), '/System/Volumes/Preboot/Cryptexes/OSlibcairo-2.dll' (no such file), '/usr/lib/libcairo-2.dll' (no such file, not in dyld cache), 'libcairo-2.dll' (no such file), '/usr/lib/libcairo-2.dll' (no such file, not in dyld cache).  Additionally, ctypes.util.find_library() did not manage to locate a library called 'libcairo-2.dll'",
    "stack": "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mOSError\u001b[0m                                   Traceback (most recent call last)\nFile \u001b[1;32m/Users/hathawayj/git/ki-analysis/Rally_87_QA/A-cerner/encounters_explore.py:2\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[39m# %%\u001b[39;00m\n\u001b[0;32m----> 2\u001b[0m ggsave(encounters_by_year,\u001b[39m\"\u001b[39;49m\u001b[39mcharts/encounters_by_year.png\u001b[39;49m\u001b[39m\"\u001b[39;49m)\n\nFile \u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/lets_plot/export/ggsave_.py:109\u001b[0m, in \u001b[0;36mggsave\u001b[0;34m(plot, filename, path, iframe, scale, w, h, unit, dpi)\u001b[0m\n\u001b[1;32m    107\u001b[0m     \u001b[39mreturn\u001b[39;00m _to_html(plot, pathname, iframe\u001b[39m=\u001b[39miframe)\n\u001b[1;32m    108\u001b[0m \u001b[39melif\u001b[39;00m ext \u001b[39min\u001b[39;00m [\u001b[39m'\u001b[39m\u001b[39mpng\u001b[39m\u001b[39m'\u001b[39m, \u001b[39m'\u001b[39m\u001b[39mpdf\u001b[39m\u001b[39m'\u001b[39m]:\n\u001b[0;32m--> 109\u001b[0m     \u001b[39mreturn\u001b[39;00m _export_as_raster(plot, pathname, scale, export_format\u001b[39m=\u001b[39;49mext, w\u001b[39m=\u001b[39;49mw, h\u001b[39m=\u001b[39;49mh, unit\u001b[39m=\u001b[39;49munit, dpi\u001b[39m=\u001b[39;49mdpi)\n\u001b[1;32m    110\u001b[0m \u001b[39melse\u001b[39;00m:\n\u001b[1;32m    111\u001b[0m     \u001b[39mraise\u001b[39;00m \u001b[39mValueError\u001b[39;00m(\n\u001b[1;32m    112\u001b[0m         \u001b[39m\"\u001b[39m\u001b[39mUnsupported file extension: \u001b[39m\u001b[39m'\u001b[39m\u001b[39m{}\u001b[39;00m\u001b[39m'\u001b[39m\u001b[39m\\n\u001b[39;00m\u001b[39mPlease use one of: \u001b[39m\u001b[39m'\u001b[39m\u001b[39mpng\u001b[39m\u001b[39m'\u001b[39m\u001b[39m, \u001b[39m\u001b[39m'\u001b[39m\u001b[39msvg\u001b[39m\u001b[39m'\u001b[39m\u001b[39m, \u001b[39m\u001b[39m'\u001b[39m\u001b[39mpdf\u001b[39m\u001b[39m'\u001b[39m\u001b[39m, \u001b[39m\u001b[39m'\u001b[39m\u001b[39mhtml\u001b[39m\u001b[39m'\u001b[39m\u001b[39m, \u001b[39m\u001b[39m'\u001b[39m\u001b[39mhtm\u001b[39m\u001b[39m'\u001b[39m\u001b[39m\"\u001b[39m\u001b[39m.\u001b[39mformat(ext)\n\u001b[1;32m    113\u001b[0m     )\n\nFile \u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/lets_plot/plot/core.py:882\u001b[0m, in \u001b[0;36m_export_as_raster\u001b[0;34m(spec, path, scale, export_format, w, h, unit, dpi)\u001b[0m\n\u001b[1;32m    879\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39m_export_as_raster\u001b[39m(spec, path, scale: \u001b[39mfloat\u001b[39m, export_format: \u001b[39mstr\u001b[39m, w\u001b[39m=\u001b[39m\u001b[39mNone\u001b[39;00m, h\u001b[39m=\u001b[39m\u001b[39mNone\u001b[39;00m, unit\u001b[39m=\u001b[39m\u001b[39mNone\u001b[39;00m, dpi\u001b[39m=\u001b[39m\u001b[39mNone\u001b[39;00m) \u001b[39m-\u001b[39m\u001b[39m>\u001b[39m Union[\n\u001b[1;32m    880\u001b[0m     \u001b[39mstr\u001b[39m, \u001b[39mNone\u001b[39;00m]:\n\u001b[1;32m    881\u001b[0m     \u001b[39mtry\u001b[39;00m:\n\u001b[0;32m--> 882\u001b[0m         \u001b[39mimport\u001b[39;00m \u001b[39mcairosvg\u001b[39;00m\n\u001b[1;32m    883\u001b[0m     \u001b[39mexcept\u001b[39;00m \u001b[39mImportError\u001b[39;00m:\n\u001b[1;32m    884\u001b[0m         \u001b[39mimport\u001b[39;00m \u001b[39msys\u001b[39;00m\n\nFile \u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/cairosvg/__init__.py:26\u001b[0m\n\u001b[1;32m     22\u001b[0m VERSION \u001b[39m=\u001b[39m __version__ \u001b[39m=\u001b[39m (ROOT \u001b[39m/\u001b[39m \u001b[39m'\u001b[39m\u001b[39mVERSION\u001b[39m\u001b[39m'\u001b[39m)\u001b[39m.\u001b[39mread_text()\u001b[39m.\u001b[39mstrip()\n\u001b[1;32m     25\u001b[0m \u001b[39m# VERSION is used in the \"url\" module imported by \"surface\"\u001b[39;00m\n\u001b[0;32m---> 26\u001b[0m \u001b[39mfrom\u001b[39;00m \u001b[39m.\u001b[39;00m \u001b[39mimport\u001b[39;00m surface  \u001b[39m# noqa isort:skip\u001b[39;00m\n\u001b[1;32m     29\u001b[0m SURFACES \u001b[39m=\u001b[39m {\n\u001b[1;32m     30\u001b[0m     \u001b[39m'\u001b[39m\u001b[39mPDF\u001b[39m\u001b[39m'\u001b[39m: surface\u001b[39m.\u001b[39mPDFSurface,\n\u001b[1;32m     31\u001b[0m     \u001b[39m'\u001b[39m\u001b[39mPNG\u001b[39m\u001b[39m'\u001b[39m: surface\u001b[39m.\u001b[39mPNGSurface,\n\u001b[0;32m   (...)\u001b[0m\n\u001b[1;32m     34\u001b[0m     \u001b[39m'\u001b[39m\u001b[39mSVG\u001b[39m\u001b[39m'\u001b[39m: surface\u001b[39m.\u001b[39mSVGSurface,\n\u001b[1;32m     35\u001b[0m }\n\u001b[1;32m     38\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39msvg2svg\u001b[39m(bytestring\u001b[39m=\u001b[39m\u001b[39mNone\u001b[39;00m, \u001b[39m*\u001b[39m, file_obj\u001b[39m=\u001b[39m\u001b[39mNone\u001b[39;00m, url\u001b[39m=\u001b[39m\u001b[39mNone\u001b[39;00m, dpi\u001b[39m=\u001b[39m\u001b[39m96\u001b[39m,\n\u001b[1;32m     39\u001b[0m             parent_width\u001b[39m=\u001b[39m\u001b[39mNone\u001b[39;00m, parent_height\u001b[39m=\u001b[39m\u001b[39mNone\u001b[39;00m, scale\u001b[39m=\u001b[39m\u001b[39m1\u001b[39m, unsafe\u001b[39m=\u001b[39m\u001b[39mFalse\u001b[39;00m,\n\u001b[1;32m     40\u001b[0m             background_color\u001b[39m=\u001b[39m\u001b[39mNone\u001b[39;00m, negate_colors\u001b[39m=\u001b[39m\u001b[39mFalse\u001b[39;00m, invert_images\u001b[39m=\u001b[39m\u001b[39mFalse\u001b[39;00m,\n\u001b[1;32m     41\u001b[0m             write_to\u001b[39m=\u001b[39m\u001b[39mNone\u001b[39;00m, output_width\u001b[39m=\u001b[39m\u001b[39mNone\u001b[39;00m, output_height\u001b[39m=\u001b[39m\u001b[39mNone\u001b[39;00m):\n\nFile \u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/cairosvg/surface.py:9\u001b[0m\n\u001b[1;32m      6\u001b[0m \u001b[39mimport\u001b[39;00m \u001b[39mcopy\u001b[39;00m\n\u001b[1;32m      7\u001b[0m \u001b[39mimport\u001b[39;00m \u001b[39mio\u001b[39;00m\n\u001b[0;32m----> 9\u001b[0m \u001b[39mimport\u001b[39;00m \u001b[39mcairocffi\u001b[39;00m \u001b[39mas\u001b[39;00m \u001b[39mcairo\u001b[39;00m\n\u001b[1;32m     11\u001b[0m \u001b[39mfrom\u001b[39;00m \u001b[39m.\u001b[39;00m\u001b[39mcolors\u001b[39;00m \u001b[39mimport\u001b[39;00m color, negate_color\n\u001b[1;32m     12\u001b[0m \u001b[39mfrom\u001b[39;00m \u001b[39m.\u001b[39;00m\u001b[39mdefs\u001b[39;00m \u001b[39mimport\u001b[39;00m (\n\u001b[1;32m     13\u001b[0m     apply_filter_after_painting, apply_filter_before_painting, clip_path,\n\u001b[1;32m     14\u001b[0m     filter_, gradient_or_pattern, linear_gradient, marker, mask, paint_mask,\n\u001b[1;32m     15\u001b[0m     parse_all_defs, pattern, prepare_filter, radial_gradient, use)\n\nFile \u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/cairocffi/__init__.py:60\u001b[0m\n\u001b[1;32m     55\u001b[0m     error_message \u001b[39m=\u001b[39m \u001b[39m'\u001b[39m\u001b[39m\\n\u001b[39;00m\u001b[39m'\u001b[39m\u001b[39m.\u001b[39mjoin(  \u001b[39m# pragma: no cover\u001b[39;00m\n\u001b[1;32m     56\u001b[0m         \u001b[39mstr\u001b[39m(exception) \u001b[39mfor\u001b[39;00m exception \u001b[39min\u001b[39;00m exceptions)\n\u001b[1;32m     57\u001b[0m     \u001b[39mraise\u001b[39;00m \u001b[39mOSError\u001b[39;00m(error_message)  \u001b[39m# pragma: no cover\u001b[39;00m\n\u001b[0;32m---> 60\u001b[0m cairo \u001b[39m=\u001b[39m dlopen(\n\u001b[1;32m     61\u001b[0m     ffi, (\u001b[39m'\u001b[39;49m\u001b[39mcairo-2\u001b[39;49m\u001b[39m'\u001b[39;49m, \u001b[39m'\u001b[39;49m\u001b[39mcairo\u001b[39;49m\u001b[39m'\u001b[39;49m, \u001b[39m'\u001b[39;49m\u001b[39mlibcairo-2\u001b[39;49m\u001b[39m'\u001b[39;49m),\n\u001b[1;32m     62\u001b[0m     (\u001b[39m'\u001b[39;49m\u001b[39mlibcairo.so.2\u001b[39;49m\u001b[39m'\u001b[39;49m, \u001b[39m'\u001b[39;49m\u001b[39mlibcairo.2.dylib\u001b[39;49m\u001b[39m'\u001b[39;49m, \u001b[39m'\u001b[39;49m\u001b[39mlibcairo-2.dll\u001b[39;49m\u001b[39m'\u001b[39;49m))\n\u001b[1;32m     65\u001b[0m \u001b[39mclass\u001b[39;00m \u001b[39m_keepref\u001b[39;00m(\u001b[39mobject\u001b[39m):  \u001b[39m# noqa: N801\u001b[39;00m\n\u001b[1;32m     66\u001b[0m \u001b[39m    \u001b[39m\u001b[39m\"\"\"Function wrapper that keeps a reference to another object.\"\"\"\u001b[39;00m\n\nFile \u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/cairocffi/__init__.py:57\u001b[0m, in \u001b[0;36mdlopen\u001b[0;34m(ffi, library_names, filenames)\u001b[0m\n\u001b[1;32m     53\u001b[0m         exceptions\u001b[39m.\u001b[39mappend(exception)\n\u001b[1;32m     55\u001b[0m error_message \u001b[39m=\u001b[39m \u001b[39m'\u001b[39m\u001b[39m\\n\u001b[39;00m\u001b[39m'\u001b[39m\u001b[39m.\u001b[39mjoin(  \u001b[39m# pragma: no cover\u001b[39;00m\n\u001b[1;32m     56\u001b[0m     \u001b[39mstr\u001b[39m(exception) \u001b[39mfor\u001b[39;00m exception \u001b[39min\u001b[39;00m exceptions)\n\u001b[0;32m---> 57\u001b[0m \u001b[39mraise\u001b[39;00m \u001b[39mOSError\u001b[39;00m(error_message)\n\n\u001b[0;31mOSError\u001b[0m: no library called \"cairo-2\" was found\nno library called \"cairo\" was found\nno library called \"libcairo-2\" was found\ncannot load library 'libcairo.so.2': dlopen(libcairo.so.2, 0x0002): tried: 'libcairo.so.2' (no such file), '/System/Volumes/Preboot/Cryptexes/OSlibcairo.so.2' (no such file), '/usr/lib/libcairo.so.2' (no such file, not in dyld cache), 'libcairo.so.2' (no such file), '/usr/lib/libcairo.so.2' (no such file, not in dyld cache).  Additionally, ctypes.util.find_library() did not manage to locate a library called 'libcairo.so.2'\ncannot load library 'libcairo.2.dylib': dlopen(libcairo.2.dylib, 0x0002): tried: 'libcairo.2.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OSlibcairo.2.dylib' (no such file), '/usr/lib/libcairo.2.dylib' (no such file, not in dyld cache), 'libcairo.2.dylib' (no such file), '/usr/lib/libcairo.2.dylib' (no such file, not in dyld cache).  Additionally, ctypes.util.find_library() did not manage to locate a library called 'libcairo.2.dylib'\ncannot load library 'libcairo-2.dll': dlopen(libcairo-2.dll, 0x0002): tried: 'libcairo-2.dll' (no such file), '/System/Volumes/Preboot/Cryptexes/OSlibcairo-2.dll' (no such file), '/usr/lib/libcairo-2.dll' (no such file, not in dyld cache), 'libcairo-2.dll' (no such file), '/usr/lib/libcairo-2.dll' (no such file, not in dyld cache).  Additionally, ctypes.util.find_library() did not manage to locate a library called 'libcairo-2.dll'"
}

Trying this command seems to show that Python doesn't see Cairo because I have the files in those locations.

 curl "https://raw.githubusercontent.com/squidfunk/mkdocs-material/master/includes/debug/cairo-lookup-macos.py" | python3 -

I tried BREW install and conda install and neither worked

I think it is an M3 issue but I can't quite figure it out. I would love help.

hathawayj commented 5 months ago

Screenshot 2024-04-28 at 5 56 09 PM

alshan commented 5 months ago

Hi, try pip3 install cairosvg. See "Install" section in cairoSVG docs.

IKupriyanov-HORIS commented 5 months ago

Hi! There is a CairoSVG issue with the workaround. Hopefully, this information may help.