| 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 | |
|---|
| 7 | import os |
|---|
| 8 | import sys |
|---|
| 9 | from fnmatch import fnmatchcase |
|---|
| 10 | from distutils.util import convert_path |
|---|
| 11 | |
|---|
| 12 | # Provided as an attribute, so you can append to these instead |
|---|
| 13 | # of replicating them: |
|---|
| 14 | standard_exclude = ('*.py', '*.pyc', '*~', '.*', '*.bak') |
|---|
| 15 | standard_exclude_directories = ('.*', 'CVS', '_darcs', './build', |
|---|
| 16 | './dist', 'EGG-INFO', '*.egg-info') |
|---|
| 17 | |
|---|
| 18 | def find_package_data( |
|---|
| 19 | where='.', package='', |
|---|
| 20 | exclude=standard_exclude, |
|---|
| 21 | exclude_directories=standard_exclude_directories, |
|---|
| 22 | only_in_packages=True, |
|---|
| 23 | show_ignored=False): |
|---|
| 24 | """ |
|---|
| 25 | Return a dictionary suitable for use in ``package_data`` |
|---|
| 26 | in a distutils ``setup.py`` file. |
|---|
| 27 | |
|---|
| 28 | The dictionary looks like:: |
|---|
| 29 | |
|---|
| 30 | {'package': [files]} |
|---|
| 31 | |
|---|
| 32 | Where ``files`` is a list of all the files in that package that |
|---|
| 33 | don't match anything in ``exclude``. |
|---|
| 34 | |
|---|
| 35 | If ``only_in_packages`` is true, then top-level directories that |
|---|
| 36 | are not packages won't be included (but directories under packages |
|---|
| 37 | will). |
|---|
| 38 | |
|---|
| 39 | Directories matching any pattern in ``exclude_directories`` will |
|---|
| 40 | be ignored; by default directories with leading ``.``, ``CVS``, |
|---|
| 41 | and ``_darcs`` will be ignored. |
|---|
| 42 | |
|---|
| 43 | If ``show_ignored`` is true, then all the files that aren't |
|---|
| 44 | included in package data are shown on stderr (for debugging |
|---|
| 45 | purposes). |
|---|
| 46 | |
|---|
| 47 | Note patterns use wildcards, or can be exact paths (including |
|---|
| 48 | leading ``./``), and all searching is case-insensitive. |
|---|
| 49 | """ |
|---|
| 50 | |
|---|
| 51 | out = {} |
|---|
| 52 | stack = [(convert_path(where), '', package, only_in_packages)] |
|---|
| 53 | while stack: |
|---|
| 54 | where, prefix, package, only_in_packages = stack.pop(0) |
|---|
| 55 | for name in os.listdir(where): |
|---|
| 56 | fn = os.path.join(where, name) |
|---|
| 57 | if os.path.isdir(fn): |
|---|
| 58 | bad_name = False |
|---|
| 59 | for pattern in exclude_directories: |
|---|
| 60 | if (fnmatchcase(name, pattern) |
|---|
| 61 | or fn.lower() == pattern.lower()): |
|---|
| 62 | bad_name = True |
|---|
| 63 | if show_ignored: |
|---|
| 64 | print >> sys.stderr, ( |
|---|
| 65 | "Directory %s ignored by pattern %s" |
|---|
| 66 | % (fn, pattern)) |
|---|
| 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 >> sys.stderr, ( |
|---|
| 88 | "File %s ignored by pattern %s" |
|---|
| 89 | % (fn, pattern)) |
|---|
| 90 | break |
|---|
| 91 | if bad_name: |
|---|
| 92 | continue |
|---|
| 93 | out.setdefault(package, []).append(prefix+name) |
|---|
| 94 | return out |
|---|
| 95 | |
|---|
| 96 | if __name__ == '__main__': |
|---|
| 97 | import pprint |
|---|
| 98 | pprint.pprint( |
|---|
| 99 | find_package_data(show_ignored=True)) |
|---|