summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/dist/dist.tex49
-rw-r--r--Lib/distutils/command/build_py.py51
-rw-r--r--Lib/distutils/dist.py1
3 files changed, 101 insertions, 0 deletions
diff --git a/Doc/dist/dist.tex b/Doc/dist/dist.tex
index 7b91809..b692c06 100644
--- a/Doc/dist/dist.tex
+++ b/Doc/dist/dist.tex
@@ -652,6 +652,55 @@ setup(...
\end{verbatim}
+\subsection{Installing Package Data}
+
+Often, additional files need to be installed into a package. These
+files are often data that's closely related to the package's
+implementation, or text files containing documentation that might be
+of interest to programmers using the package. These files are called
+\dfn{package data}.
+
+Package data can be added to packages using the \code{package_data}
+keyword argument to the \function{setup()} function. The value must
+be a mapping from package name to a list of relative path names that
+should be copied into the package. The paths are interpreted as
+relative to the directory containing the package (information from the
+\code{package_dir} mapping is used if appropriate); that is, the files
+are expected to be part of the package in the source directories.
+They may contain glob patterns as well.
+
+The path names may contain directory portions; any necessary
+directories will be created in the installation.
+
+For example, if a package should contain a subdirectory with several
+data files, the files can be arranged like this in the source tree:
+
+\begin{verbatim}
+setup.py
+src/
+ mypkg/
+ __init__.py
+ module.py
+ data/
+ tables.dat
+ spoons.dat
+ forks.dat
+\end{verbatim}
+
+The corresponding call to \function{setup()} might be:
+
+\begin{verbatim}
+setup(...,
+ packages=['mypkg'],
+ package_dir={'mypkg': 'src/mypkg'},
+ package_data={'pypkg': ['data/*.dat']},
+ )
+\end{verbatim}
+
+
+\versionadded{2.4}
+
+
\subsection{Installing Additional Files}
The \option{data\_files} option can be used to specify additional
diff --git a/Lib/distutils/command/build_py.py b/Lib/distutils/command/build_py.py
index 6c007c6..329b55a 100644
--- a/Lib/distutils/command/build_py.py
+++ b/Lib/distutils/command/build_py.py
@@ -37,6 +37,7 @@ class build_py (Command):
self.build_lib = None
self.py_modules = None
self.package = None
+ self.package_data = None
self.package_dir = None
self.compile = 0
self.optimize = 0
@@ -51,6 +52,8 @@ class build_py (Command):
# options -- list of packages and list of modules.
self.packages = self.distribution.packages
self.py_modules = self.distribution.py_modules
+ self.package_data = self.distribution.package_data
+ self.data_files = self.get_data_files()
self.package_dir = {}
if self.distribution.package_dir:
for name, path in self.distribution.package_dir.items():
@@ -92,11 +95,53 @@ class build_py (Command):
self.build_modules()
if self.packages:
self.build_packages()
+ self.build_package_data()
self.byte_compile(self.get_outputs(include_bytecode=0))
# run ()
+ def get_data_files (self):
+ """Generate list of '(package,src_dir,build_dir,filenames)' tuples"""
+ data = []
+ for package in self.packages:
+ # Locate package source directory
+ src_dir = self.get_package_dir(package)
+
+ # Compute package build directory
+ build_dir = os.path.join(*([self.build_lib] + package.split('.')))
+
+ # Length of path to strip from found files
+ plen = len(src_dir)+1
+
+ # Strip directory from globbed filenames
+ filenames = [
+ file[plen:] for file in self.find_data_files(package, src_dir)
+ ]
+ data.append((package, src_dir, build_dir, filenames))
+ return data
+
+ def find_data_files (self, package, src_dir):
+ """Return filenames for package's data files in 'src_dir'"""
+ globs = (self.package_data.get('', [])
+ + self.package_data.get(package, []))
+ files = []
+ for pattern in globs:
+ # Each pattern has to be converted to a platform-specific path
+ filelist = glob(os.path.join(src_dir, convert_path(pattern)))
+ # Files that match more than one pattern are only added once
+ files.extend([fn for fn in filelist if fn not in files])
+ return files
+
+ def build_package_data (self):
+ """Copy data files into build directory"""
+ lastdir = None
+ for package, src_dir, build_dir, filenames in self.data_files:
+ for filename in filenames:
+ target = os.path.join(build_dir, filename)
+ self.mkpath(os.path.dirname(target))
+ self.copy_file(os.path.join(src_dir, filename), target,
+ preserve_mode=False)
def get_package_dir (self, package):
"""Return the directory, relative to the top of the source
@@ -304,6 +349,12 @@ class build_py (Command):
if self.optimize > 0:
outputs.append(filename + "o")
+ outputs += [
+ os.path.join(build_dir, filename)
+ for package, src_dir, build_dir, filenames in self.data_files
+ for filename in filenames
+ ]
+
return outputs
diff --git a/Lib/distutils/dist.py b/Lib/distutils/dist.py
index 2795b7b..7d0a7ba 100644
--- a/Lib/distutils/dist.py
+++ b/Lib/distutils/dist.py
@@ -158,6 +158,7 @@ class Distribution:
# than of the Distribution itself. We provide aliases for them in
# Distribution as a convenience to the developer.
self.packages = None
+ self.package_data = {}
self.package_dir = None
self.py_modules = None
self.libraries = None