How do I distribute fonts with my python package?

匿名 (未验证) 提交于 2019-12-03 03:03:02

问题:

I have created a package called clearplot that wraps around matplotlib. I have also created a nice font that I want to distribute with my package. I consulted this section of the Python Packaging User guide, and determined that I should use the data_files keyword. I chose data_files instead of package_data since I need to install the font in a matplotlib directory that is outside of my package.

Here is my first, flawed, attempt at a setup.py file:

from distutils.core import setup import os, sys import matplotlib as mpl  #Find where matplotlib stores its True Type fonts mpl_data_dir = os.path.dirname(mpl.matplotlib_fname()) mpl_ttf_dir = os.path.join(mpl_data_dir, 'fonts', 'ttf')  setup(     ...(edited for brevity)...     install_requires = ['matplotlib >= 1.4.0, !=1.4.3', 'numpy >= 1.6'],     data_files = [         (mpl_ttf_dir, ['./font_files/TeXGyreHeros-txfonts/TeXGyreHerosTXfonts-Regular.ttf']),         (mpl_ttf_dir, ['./font_files/TeXGyreHeros-txfonts/TeXGyreHerosTXfonts-Italic.ttf'])] )  #Try to delete matplotlib's fontList cache mpl_cache_dir = mpl.get_cachedir() mpl_cache_dir_ls = os.listdir(mpl_cache_dir) if 'fontList.cache' in mpl_cache_dir_ls:     fontList_path = os.path.join(mpl_cache_dir, 'fontList.cache')     os.remove(fontList_path) 

There are two issues with this setup.py:

  1. I attempt to import matplotlib before setup() has a chance to install it. This is an obvious booboo, but I needed to know where mpl_ttf_dir was before I ran setup().
  2. As mentioned here, wheel distributions do not support absolute paths for data_files. I didn't think this would be a problem because I thought I would just use a sdist distribution. (sdists do allow absolute paths.) Then I came to find out that pip 7.0 (and later) converts all packages to wheel distributions, even if the distribution was originally created as a sdist.

I was quite annoyed by issue #2, but, since then, I found out that absolute paths are bad because they do not work with virtualenv. Thus, I am now willing to change my approach, but what do I do?

The only idea I have is to distribute the font as package_data first and then move the font to the proper location afterwards using the os module. Is that a kosher method?

回答1:

Thanks to @benjaoming's answer and this blog post, here is what I came up with:

from setuptools import setup from setuptools.command.install import install import warnings  #Set up the machinery to install custom fonts.  Subclass the setup tools install  #class in order to run custom commands during installation.   class move_ttf(install):     def run(self):         """         Performs the usual install process and then copies the True Type fonts          that come with clearplot into matplotlib's True Type font directory,          and deletes the matplotlib fontList.cache          """         #Perform the usual install process         install.run(self)         #Try to install custom fonts         try:             import os, shutil             import matplotlib as mpl             import clearplot as cp              #Find where matplotlib stores its True Type fonts             mpl_data_dir = os.path.dirname(mpl.matplotlib_fname())             mpl_ttf_dir = os.path.join(mpl_data_dir, 'fonts', 'ttf')              #Copy the font files to matplotlib's True Type font directory             #(I originally tried to move the font files instead of copy them,             #but it did not seem to work, so I gave up.)             cp_ttf_dir = os.path.join(os.path.dirname(cp.__file__), 'true_type_fonts')             for file_name in os.listdir(cp_ttf_dir):                 if file_name[-4:] == '.ttf':                     old_path = os.path.join(cp_ttf_dir, file_name)                     new_path = os.path.join(mpl_ttf_dir, file_name)                     shutil.copyfile(old_path, new_path)                     print "Copying " + old_path + " -> " + new_path              #Try to delete matplotlib's fontList cache             mpl_cache_dir = mpl.get_cachedir()             mpl_cache_dir_ls = os.listdir(mpl_cache_dir)             if 'fontList.cache' in mpl_cache_dir_ls:                 fontList_path = os.path.join(mpl_cache_dir, 'fontList.cache')                 os.remove(fontList_path)                 print "Deleted the matplotlib fontList.cache"         except:             warnings.warn("WARNING: An issue occured while installing the custom fonts for clearplot.")  setup(...     #Specify the dependencies and versions     install_requires = ['matplotlib >= 1.4.0, !=1.4.3', 'numpy >= 1.6'],     #Specify any non-python files to be distributed with the package     package_data = {'' : ['color_maps/*.csv', 'true_type_fonts/*.ttf']},     #Specify the custom install class     cmdclass={'install' : move_ttf} ) 

This solves both problem #1 (it installs matplotlib before it imports it) and problem #2 (it works with wheels).



回答2:

The only idea I have is to distribute the font as package_data first and then move the font to the proper location afterwards using the os module. Is that a kosher method?

I would consider doing exactly this. I know your package may not be an obvious candidate for virtualenvs, but consider that python packages may be installed only to a user-writable location. Thus, copying the font when you first run your programme and detecting the correct location, might prompt you to do stuff in a better manner than possible through setup.py, stuff like: Elevate privileges through a password prompt in case it's needed, ask for a different location in case you fail to detect it, prompt if you are over-writing existing system files etc.

I once tried arguing that Python packages should be able to place stuff in /etc, but I realized the benefits were small compared to just creating a proper native package for the target OS, i.e. a debian package for Debian or a .exe installer for Windows.

The bottom line is that wheel and setuptools are not package managers for your entire OS, but just for what's in some local site-packages/.

I hope this answer gives you enough background to avoid data_files. One last good reason: Making it work across distutils, setuptools, and wheel is a no-go.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!