1# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) 2# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 3# Note: you may want to copy this into your setup.py file verbatim, as 4# you can't import this from another package, when you don't know if 5# that package is installed yet. 6 7from __future__ import print_function 8import os 9import sys 10from fnmatch import fnmatchcase 11from distutils.util import convert_path 12 13# Provided as an attribute, so you can append to these instead 14# of replicating them: 15standard_exclude = ('*.py', '*.pyc', '*$py.class', '*~', '.*', '*.bak') 16standard_exclude_directories = ('.*', 'CVS', '_darcs', './build', 17 './dist', 'EGG-INFO', '*.egg-info') 18 19def find_package_data( 20 where='.', package='', 21 exclude=standard_exclude, 22 exclude_directories=standard_exclude_directories, 23 only_in_packages=True, 24 show_ignored=False): 25 """ 26 Return a dictionary suitable for use in ``package_data`` 27 in a distutils ``setup.py`` file. 28 29 The dictionary looks like:: 30 31 {'package': [files]} 32 33 Where ``files`` is a list of all the files in that package that 34 don't match anything in ``exclude``. 35 36 If ``only_in_packages`` is true, then top-level directories that 37 are not packages won't be included (but directories under packages 38 will). 39 40 Directories matching any pattern in ``exclude_directories`` will 41 be ignored; by default directories with leading ``.``, ``CVS``, 42 and ``_darcs`` will be ignored. 43 44 If ``show_ignored`` is true, then all the files that aren't 45 included in package data are shown on stderr (for debugging 46 purposes). 47 48 Note patterns use wildcards, or can be exact paths (including 49 leading ``./``), and all searching is case-insensitive. 50 """ 51 52 out = {} 53 stack = [(convert_path(where), '', package, only_in_packages)] 54 while stack: 55 where, prefix, package, only_in_packages = stack.pop(0) 56 for name in os.listdir(where): 57 fn = os.path.join(where, name) 58 if os.path.isdir(fn): 59 bad_name = False 60 for pattern in exclude_directories: 61 if (fnmatchcase(name, pattern) 62 or fn.lower() == pattern.lower()): 63 bad_name = True 64 if show_ignored: 65 print("Directory %s ignored by pattern %s" 66 % (fn, pattern), file=sys.stderr) 67 break 68 if bad_name: 69 continue 70 if (os.path.isfile(os.path.join(fn, '__init__.py')) 71 and not prefix): 72 if not package: 73 new_package = name 74 else: 75 new_package = package + '.' + name 76 stack.append((fn, '', new_package, False)) 77 else: 78 stack.append((fn, prefix + name + '/', package, only_in_packages)) 79 elif package or not only_in_packages: 80 # is a file 81 bad_name = False 82 for pattern in exclude: 83 if (fnmatchcase(name, pattern) 84 or fn.lower() == pattern.lower()): 85 bad_name = True 86 if show_ignored: 87 print("File %s ignored by pattern %s" 88 % (fn, pattern), file=sys.stderr) 89 break 90 if bad_name: 91 continue 92 out.setdefault(package, []).append(prefix+name) 93 return out 94 95if __name__ == '__main__': 96 import pprint 97 pprint.pprint( 98 find_package_data(show_ignored=True)) 99