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)) |
---|