Home | History | Annotate | Download | only in util
      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 from __future__ import print_function
      8 import os
      9 import sys
     10 from fnmatch import fnmatchcase
     11 from distutils.util import convert_path
     12 
     13 # Provided as an attribute, so you can append to these instead
     14 # of replicating them:
     15 standard_exclude = ('*.py', '*.pyc', '*$py.class', '*~', '.*', '*.bak')
     16 standard_exclude_directories = ('.*', 'CVS', '_darcs', './build',
     17                                 './dist', 'EGG-INFO', '*.egg-info')
     18 
     19 def 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 
     95 if __name__ == '__main__':
     96     import pprint
     97     pprint.pprint(
     98         find_package_data(show_ignored=True))
     99