def _deduce_app_name(self): """ If an app name is not provided, try and figure one out. """ # Check and see if we can use the destination as a valid # name. use_dest = True if file_exists(self.dest) and os.path.isdir(self.dest): use_dest = False elif file_exists(self.dest) and os.path.isfile(self.dest): # Confirm overwrite use_dest = True if use_dest: app_name = os.path.basename(self.dest) self.dest = os.path.dirname(self.dest) else: if self.src_directory.endswith('/'): tmp_src_directory = self.src_directory[:-1] else: tmp_src_directory = self.src_directory app_name = '{0}.pyz'.format(os.path.basename(tmp_src_directory)) if not app_name: raise ZapperError('Unable to deduce app name!') return app_name
def _read_build_file(build_file_path): """ Read the 'build' file. Args: build_file_path (str): The path to the build file Returns: dict containing contents of 'build' file. Raises: IOError if the provided build file doesn't exist. ValueError if build file does not contain 'zapper' key. """ # Check if we're given a file handle. If we are, just read it. # otherwise, do all our normal file operations. if isinstance(build_file_path, file): build_data = yaml.load(build_file_path.read()) else: if not file_exists(build_file_path): return {} # Read a build file located in the source path. with open(build_file_path, 'r') as f: build_data = yaml.load(f.read()) # We require that the build file have a zapper "root" key, # so we'll raise a ValueError if it does not. if 'zapper' not in build_data: raise ValueError('"{0}" does not contain a "zapper" key!'.format(build_file_path)) return build_data['zapper']
def _find_build_file(src_path): """ Search for a 'build' file. Args: src_path (str): The path to the search. Returns: str containing full path to a build file. Raises: ValueError if we can't find a build file. """ # Possible names for the build file. 'build' and 'build.yml' # seemed sensible enough. possible_build_file_names = ['build', 'build.yml', 'build.yaml'] # Look for a file named either 'build' or 'build.yml' build_file = None for build_file_name in possible_build_file_names: build_file_path = os.path.join(src_path, build_file_name) if file_exists(build_file_path): build_file = build_file_path break # If we get here, and don't have a build file, just return None. return build_file
def _clean(self): """ Clean up after myself. """ self._debug('Cleaning up') # Loop through all files we've created, and attempt # to remove them. for fpath in self.files_created: if not file_exists(fpath): continue self._debug('Removing "{0}"'.format(fpath)) if os.path.isdir(fpath): rmtree(fpath) else: os.remove(fpath)
def _parse_args(): """ Parse Command Line Args. Returns: argparse object. Raises: ValueError if 'src' path is invalid. """ # Set up our argument parser. parser = argparse.ArgumentParser( description='A tool to build python zipapps.', usage='%(prog)s SRC_PATH [DEST_PATH]', epilog='NOTE: Any options specified on the command line will override ALL defined builds.' 'This means if you specify "app_name" on the command line, and you have multiple ' 'builds specied in the "build_file", that "app_name" will be applied to all resulting ' 'artifacts.' ) parser.add_argument('src_path', nargs=1, type=str, help='Path to the app to build.') parser.add_argument('dest_path', nargs='?', type=str, help='The desired destination.') parser.add_argument('-b', '--build-file', type=argparse.FileType(), help='Path to a build file.') parser.add_argument('-n', '--name', type=str, help='Specify a name for the application. By default basename is used.') parser.add_argument('-e', '--entry-point', type=str, help='The entry point for the application.') parser.add_argument('-r', '--requirements', type=str, help='Comma separated .ist of requirements. ' 'Prepend with an "@" sign to specify a requirements file') parser.add_argument('--ignore', type=str, help='Comma separated list of files/directories to ignore.') parser.add_argument('--leave-pyc', action='store_true', default=False, help='Toggles NOT cleaning out any pyc files.') parser.add_argument('--python-shebang', type=str, help='Specify a nonstandard python-shebang.') parser.add_argument('-v', '--verbose', action='store_true', default=False, help='Toggle verbosity.') args = parser.parse_args() # Ensure the provided Source Path exists, and raise a # ValueError if it does not. args.src_path = args.src_path[0] if not file_exists(args.src_path): raise ValueError('"{0}" does not exist or is not readable!' .format(args.src_path)) return args
def _install_requirements(self): """ Install specified dependencies to a 'vendor' directory. If requirements are defined in the build file, install those to a 'vendor' directory. If they are not defined, read the 'requirements.txt' file (if it exists) and install those to a 'vendor' directory. Raises: OSError if Pip is not installed. """ # On windows, pip is 'pip.exe', but everywhere else its pip. if os.name == 'posix': pip_name = 'pip' else: pip_name = 'pip.exe' # Test if Pip is installed and bomb if it's not. pip_cmd = which(pip_name) if not pip_cmd: raise ZapperError('Required program "pip" not installed!') # Check if our vendor path exists, and if it doesn't, # create it. This way if a project already has one, # I don't step on too many toes. self._debug('Installing Dependencies.') if not file_exists(self.vendor_path): self._debug('Creating "{0}"'.format(self.vendor_path)) os.makedirs(self.vendor_path) self.files_created.append(self.vendor_path) # Loop through provided requirements and install # run 'pip install' if self.requirements: self._debug('Requrements List provided.') for requirement in self.requirements: cmd = [ pip_cmd, 'install', '{0}'.format(requirement), '--target={0}'.format(self.vendor_path), ] self._debug('Installing "{0}"" with command "{1}"' .format(requirement, self.vendor_path)) try: output = subprocess.check_output(cmd) except subprocess.CalledProcessError as e: raise ZapperError( 'Failed while installing dependencies! Error: "{0}"' .format(e)) self._debug('{0}'.format(output)) # If a requirements.txt file is provided, feed it to 'pip' # and install everything to the vendor directory. if self.requirements_txt: if not file_exists(self.requirements_txt): self._debug('"requirements.txt" not found at: "{0}"' .format(self.requirements_txt)) return cmd = [ 'pip', 'install', '-r', '{0}'.format(self.requirements_txt), '--target={0}'.format(self.vendor_path), ] self._debug('Running command: "{0}"'.format(cmd)) try: output = subprocess.check_output(cmd) except subprocess.CalledProcessError as e: raise ZapperError( 'Failed while installing dependencies! Error: "{0}"' .format(e)) self._debug('{0}'.format(output))