Statistics
| Branch: | Revision:

root / env / lib / python2.7 / site-packages / distribute-0.6.19-py2.7.egg / setuptools / command / build_py.py @ 1a305335

History | View | Annotate | Download (9.71 KB)

1
import os.path, sys, fnmatch
2
from distutils.command.build_py import build_py as _build_py
3
from distutils.util import convert_path
4
from glob import glob
5

    
6
try:
7
    from distutils.util import Mixin2to3 as _Mixin2to3
8
    # add support for converting doctests that is missing in 3.1 distutils
9
    from distutils import log
10
    from lib2to3.refactor import RefactoringTool, get_fixers_from_package
11
    import setuptools
12
    class DistutilsRefactoringTool(RefactoringTool):
13
        def log_error(self, msg, *args, **kw):
14
            log.error(msg, *args)
15

    
16
        def log_message(self, msg, *args):
17
            log.info(msg, *args)
18

    
19
        def log_debug(self, msg, *args):
20
            log.debug(msg, *args)
21

    
22
    class Mixin2to3(_Mixin2to3):
23
        def run_2to3(self, files, doctests = False):
24
            # See of the distribution option has been set, otherwise check the
25
            # setuptools default.
26
            if self.distribution.use_2to3 is not True:
27
                return
28
            if not files:
29
                return
30
            log.info("Fixing "+" ".join(files))
31
            if not self.fixer_names:
32
                self.fixer_names = []
33
                for p in setuptools.lib2to3_fixer_packages:
34
                    self.fixer_names.extend(get_fixers_from_package(p))
35
                if self.distribution.use_2to3_fixers is not None:
36
                    for p in self.distribution.use_2to3_fixers:
37
                        self.fixer_names.extend(get_fixers_from_package(p))
38
            if doctests:
39
                if setuptools.run_2to3_on_doctests:
40
                    r = DistutilsRefactoringTool(self.fixer_names)
41
                    r.refactor(files, write=True, doctests_only=True)
42
            else:
43
                _Mixin2to3.run_2to3(self, files)
44

    
45
except ImportError:
46
    class Mixin2to3:
47
        def run_2to3(self, files, doctests=True):
48
            # Nothing done in 2.x
49
            pass
50

    
51
class build_py(_build_py, Mixin2to3):
52
    """Enhanced 'build_py' command that includes data files with packages
53

54
    The data files are specified via a 'package_data' argument to 'setup()'.
55
    See 'setuptools.dist.Distribution' for more details.
56

57
    Also, this version of the 'build_py' command allows you to specify both
58
    'py_modules' and 'packages' in the same setup operation.
59
    """
60
    def finalize_options(self):
61
        _build_py.finalize_options(self)
62
        self.package_data = self.distribution.package_data
63
        self.exclude_package_data = self.distribution.exclude_package_data or {}
64
        if 'data_files' in self.__dict__: del self.__dict__['data_files']
65
        self.__updated_files = []
66
        self.__doctests_2to3 = []
67

    
68
    def run(self):
69
        """Build modules, packages, and copy data files to build directory"""
70
        if not self.py_modules and not self.packages:
71
            return
72

    
73
        if self.py_modules:
74
            self.build_modules()
75

    
76
        if self.packages:
77
            self.build_packages()
78
            self.build_package_data()
79

    
80
        self.run_2to3(self.__updated_files, False)
81
        self.run_2to3(self.__updated_files, True)
82
        self.run_2to3(self.__doctests_2to3, True)
83

    
84
        # Only compile actual .py files, using our base class' idea of what our
85
        # output files are.
86
        self.byte_compile(_build_py.get_outputs(self, include_bytecode=0))
87

    
88
    def __getattr__(self,attr):
89
        if attr=='data_files':  # lazily compute data files
90
            self.data_files = files = self._get_data_files(); return files
91
        return _build_py.__getattr__(self,attr)
92

    
93
    def build_module(self, module, module_file, package):
94
        outfile, copied = _build_py.build_module(self, module, module_file, package)
95
        if copied:
96
            self.__updated_files.append(outfile)
97
        return outfile, copied
98

    
99
    def _get_data_files(self):
100
        """Generate list of '(package,src_dir,build_dir,filenames)' tuples"""
101
        self.analyze_manifest()
102
        data = []
103
        for package in self.packages or ():
104
            # Locate package source directory
105
            src_dir = self.get_package_dir(package)
106

    
107
            # Compute package build directory
108
            build_dir = os.path.join(*([self.build_lib] + package.split('.')))
109

    
110
            # Length of path to strip from found files
111
            plen = len(src_dir)+1
112

    
113
            # Strip directory from globbed filenames
114
            filenames = [
115
                file[plen:] for file in self.find_data_files(package, src_dir)
116
                ]
117
            data.append( (package, src_dir, build_dir, filenames) )
118
        return data
119

    
120
    def find_data_files(self, package, src_dir):
121
        """Return filenames for package's data files in 'src_dir'"""
122
        globs = (self.package_data.get('', [])
123
                 + self.package_data.get(package, []))
124
        files = self.manifest_files.get(package, [])[:]
125
        for pattern in globs:
126
            # Each pattern has to be converted to a platform-specific path
127
            files.extend(glob(os.path.join(src_dir, convert_path(pattern))))
128
        return self.exclude_data_files(package, src_dir, files)
129

    
130
    def build_package_data(self):
131
        """Copy data files into build directory"""
132
        lastdir = None
133
        for package, src_dir, build_dir, filenames in self.data_files:
134
            for filename in filenames:
135
                target = os.path.join(build_dir, filename)
136
                self.mkpath(os.path.dirname(target))
137
                srcfile = os.path.join(src_dir, filename)
138
                outf, copied = self.copy_file(srcfile, target)
139
                srcfile = os.path.abspath(srcfile)
140
                if copied and srcfile in self.distribution.convert_2to3_doctests:
141
                    self.__doctests_2to3.append(outf)
142

    
143

    
144
    def analyze_manifest(self):
145
        self.manifest_files = mf = {}
146
        if not self.distribution.include_package_data:
147
            return
148
        src_dirs = {}
149
        for package in self.packages or ():
150
            # Locate package source directory
151
            src_dirs[assert_relative(self.get_package_dir(package))] = package
152

    
153
        self.run_command('egg_info')
154
        ei_cmd = self.get_finalized_command('egg_info')
155
        for path in ei_cmd.filelist.files:
156
            d,f = os.path.split(assert_relative(path))
157
            prev = None
158
            oldf = f
159
            while d and d!=prev and d not in src_dirs:
160
                prev = d
161
                d, df = os.path.split(d)
162
                f = os.path.join(df, f)
163
            if d in src_dirs:
164
                if path.endswith('.py') and f==oldf:
165
                    continue    # it's a module, not data
166
                mf.setdefault(src_dirs[d],[]).append(path)
167

    
168
    def get_data_files(self): pass  # kludge 2.4 for lazy computation
169

    
170
    if sys.version<"2.4":    # Python 2.4 already has this code
171
        def get_outputs(self, include_bytecode=1):
172
            """Return complete list of files copied to the build directory
173

174
            This includes both '.py' files and data files, as well as '.pyc'
175
            and '.pyo' files if 'include_bytecode' is true.  (This method is
176
            needed for the 'install_lib' command to do its job properly, and to
177
            generate a correct installation manifest.)
178
            """
179
            return _build_py.get_outputs(self, include_bytecode) + [
180
                os.path.join(build_dir, filename)
181
                for package, src_dir, build_dir,filenames in self.data_files
182
                for filename in filenames
183
                ]
184

    
185
    def check_package(self, package, package_dir):
186
        """Check namespace packages' __init__ for declare_namespace"""
187
        try:
188
            return self.packages_checked[package]
189
        except KeyError:
190
            pass
191

    
192
        init_py = _build_py.check_package(self, package, package_dir)
193
        self.packages_checked[package] = init_py
194

    
195
        if not init_py or not self.distribution.namespace_packages:
196
            return init_py
197

    
198
        for pkg in self.distribution.namespace_packages:
199
            if pkg==package or pkg.startswith(package+'.'):
200
                break
201
        else:
202
            return init_py
203

    
204
        f = open(init_py,'rU')
205
        if 'declare_namespace' not in f.read():
206
            from distutils import log
207
            log.warn(
208
               "WARNING: %s is a namespace package, but its __init__.py does\n"
209
               "not declare_namespace(); setuptools 0.7 will REQUIRE this!\n"
210
               '(See the setuptools manual under "Namespace Packages" for '
211
               "details.)\n", package
212
            )
213
        f.close()
214
        return init_py
215

    
216
    def initialize_options(self):
217
        self.packages_checked={}
218
        _build_py.initialize_options(self)
219

    
220

    
221
    def get_package_dir(self, package):
222
        res = _build_py.get_package_dir(self, package)
223
        if self.distribution.src_root is not None:
224
            return os.path.join(self.distribution.src_root, res)
225
        return res
226

    
227

    
228
    def exclude_data_files(self, package, src_dir, files):
229
        """Filter filenames for package's data files in 'src_dir'"""
230
        globs = (self.exclude_package_data.get('', [])
231
                 + self.exclude_package_data.get(package, []))
232
        bad = []
233
        for pattern in globs:
234
            bad.extend(
235
                fnmatch.filter(
236
                    files, os.path.join(src_dir, convert_path(pattern))
237
                )
238
            )
239
        bad = dict.fromkeys(bad)
240
        seen = {}
241
        return [
242
            f for f in files if f not in bad
243
                and f not in seen and seen.setdefault(f,1)  # ditch dupes
244
        ]
245

    
246

    
247
def assert_relative(path):
248
    if not os.path.isabs(path):
249
        return path
250
    from distutils.errors import DistutilsSetupError
251
    raise DistutilsSetupError(
252
"""Error: setup script specifies an absolute path:
253

254
    %s
255

256
setup() arguments must *always* be /-separated paths relative to the
257
setup.py directory, *never* absolute paths.
258
""" % path
259
    )
260

    
261

    
262

    
263

    
264

    
265

    
266

    
267

    
268