def handle_extra_path(self): """Set `path_file` and `extra_dirs` using `extra_path`.""" if self.extra_path is None: self.extra_path = self.distribution.extra_path if self.extra_path is not None: if isinstance(self.extra_path, str): self.extra_path = self.extra_path.split(',') if len(self.extra_path) == 1: path_file = extra_dirs = self.extra_path[0] elif len(self.extra_path) == 2: path_file, extra_dirs = self.extra_path else: raise PackagingOptionError( "'extra_path' option must be a list, tuple, or " "comma-separated string with 1 or 2 elements") # convert to local form in case Unix notation used (as it # should be in setup scripts) extra_dirs = convert_path(extra_dirs) else: path_file = None extra_dirs = '' # XXX should we warn if path_file and not extra_dirs? (in which # case the path file would be harmless but pointless) self.path_file = path_file self.extra_dirs = extra_dirs
def _parse_template_line(self, line): words = line.split() if len(words) == 1 and words[0] not in ( 'include', 'exclude', 'global-include', 'global-exclude', 'recursive-include', 'recursive-exclude', 'graft', 'prune'): # no action given, let's use the default 'include' words.insert(0, 'include') action = words[0] patterns = dir = dir_pattern = None if action in ('include', 'exclude', 'global-include', 'global-exclude'): if len(words) < 2: raise PackagingTemplateError( "%r expects <pattern1> <pattern2> ..." % action) patterns = [convert_path(word) for word in words[1:]] elif action in ('recursive-include', 'recursive-exclude'): if len(words) < 3: raise PackagingTemplateError( "%r expects <dir> <pattern1> <pattern2> ..." % action) dir = convert_path(words[1]) patterns = [convert_path(word) for word in words[2:]] elif action in ('graft', 'prune'): if len(words) != 2: raise PackagingTemplateError( "%r expects a single <dir_pattern>" % action) dir_pattern = convert_path(words[1]) else: raise PackagingTemplateError("unknown action %r" % action) return action, patterns, dir, dir_pattern
def find_data_files(self, package, src_dir): """Return filenames for package's data files in 'src_dir'. Helper function for get_data_files. """ 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 run(self): self.mkpath(self.install_dir) for _file in self.data_files.items(): destination = convert_path(self.expand_categories(_file[1])) dir_dest = os.path.abspath(os.path.dirname(destination)) self.mkpath(dir_dest) try: out = self.copy_file(_file[0], dir_dest)[0] except Error as e: logger.warning('%s: %s', self.get_command_name(), e) out = destination self.outfiles.append(out) self.data_files_out.append((_file[0], destination))
def finalize_options(self): self.set_undefined_options('build', 'use_2to3', 'use_2to3_fixers', 'convert_2to3_doctests', 'build_lib', 'force') # Get the distribution options that are aliases for build_py # 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.package_dir = None if self.distribution.package_dir is not None: self.package_dir = convert_path(self.distribution.package_dir) self.data_files = self.get_data_files() # Ick, copied straight from install_lib.py (fancy_getopt needs a # type system! Hell, *everything* needs a type system!!!) if not isinstance(self.optimize, int): try: self.optimize = int(self.optimize) assert 0 <= self.optimize <= 2 except (ValueError, AssertionError): raise PackagingOptionError("optimize must be 0, 1, or 2")
def create_user_dirs(self): """Create directories under USERBASE as needed.""" home = convert_path(os.path.expanduser("~")) for name, path in self.config_vars.items(): if path.startswith(home) and not os.path.isdir(path): os.makedirs(path, 0o700)
def convert_paths(self, *names): """Call `convert_path` over `names`.""" for name in names: attr = "install_" + name setattr(self, attr, convert_path(getattr(self, attr)))
def copy_scripts(self): """Copy each script listed in 'self.scripts'; if it's marked as a Python script in the Unix way (first line matches 'first_line_re', ie. starts with "\#!" and contains "python"), then adjust the first line to refer to the current Python interpreter as we copy. """ self.mkpath(self.build_dir) outfiles = [] for script in self.scripts: adjust = False script = convert_path(script) outfile = os.path.join(self.build_dir, os.path.basename(script)) outfiles.append(outfile) if not self.force and not newer(script, outfile): logger.debug("not copying %s (up-to-date)", script) continue # Always open the file, but ignore failures in dry-run mode -- # that way, we'll get accurate feedback if we can read the # script. try: f = open(script, "rb") except IOError: if not self.dry_run: raise f = None else: encoding, lines = detect_encoding(f.readline) f.seek(0) first_line = f.readline() if not first_line: logger.warning('%s: %s is an empty file (skipping)', self.get_command_name(), script) continue match = first_line_re.match(first_line) if match: adjust = True post_interp = match.group(1) or b'' if adjust: logger.info("copying and adjusting %s -> %s", script, self.build_dir) if not self.dry_run: if not sysconfig.is_python_build(): executable = self.executable else: executable = os.path.join( sysconfig.get_config_var("BINDIR"), "python%s%s" % (sysconfig.get_config_var("VERSION"), sysconfig.get_config_var("EXE"))) executable = os.fsencode(executable) shebang = b"#!" + executable + post_interp + b"\n" # Python parser starts to read a script using UTF-8 until # it gets a #coding:xxx cookie. The shebang has to be the # first line of a file, the #coding:xxx cookie cannot be # written before. So the shebang has to be decodable from # UTF-8. try: shebang.decode('utf-8') except UnicodeDecodeError: raise ValueError( "The shebang ({!r}) is not decodable " "from utf-8".format(shebang)) # If the script is encoded to a custom encoding (use a # #coding:xxx cookie), the shebang has to be decodable from # the script encoding too. try: shebang.decode(encoding) except UnicodeDecodeError: raise ValueError( "The shebang ({!r}) is not decodable " "from the script encoding ({})" .format(shebang, encoding)) with open(outfile, "wb") as outf: outf.write(shebang) outf.writelines(f.readlines()) if f: f.close() else: if f: f.close() self.copy_file(script, outfile) if os.name == 'posix': for file in outfiles: if self.dry_run: logger.info("changing mode of %s", file) else: oldmode = os.stat(file).st_mode & 0o7777 newmode = (oldmode | 0o555) & 0o7777 if newmode != oldmode: logger.info("changing mode of %s from %o to %o", file, oldmode, newmode) os.chmod(file, newmode) return outfiles