def consolidate_dependencies(needs_ipython, child_program, requirement_files, manual_dependencies): """Parse files, get deps and merge them. Deps read later overwrite those read earlier.""" # We get the logger here because it's not defined at module level logger = logging.getLogger('fades') if needs_ipython: logger.debug( "Adding ipython dependency because --ipython was detected") ipython_dep = parsing.parse_manual(['ipython']) else: ipython_dep = {} if child_program: srcfile_deps = parsing.parse_srcfile(child_program) logger.debug("Dependencies from source file: %s", srcfile_deps) docstring_deps = parsing.parse_docstring(child_program) logger.debug("Dependencies from docstrings: %s", docstring_deps) else: srcfile_deps = {} docstring_deps = {} all_dependencies = [ipython_dep, srcfile_deps, docstring_deps] if requirement_files is not None: for rf_path in requirement_files: rf_deps = parsing.parse_reqfile(rf_path) logger.debug('Dependencies from requirements file %r: %s', rf_path, rf_deps) all_dependencies.append(rf_deps) manual_deps = parsing.parse_manual(manual_dependencies) logger.debug("Dependencies from parameters: %s", manual_deps) all_dependencies.append(manual_deps) # Merge dependencies indicated_deps = {} for dep in all_dependencies: for repo, info in dep.items(): indicated_deps.setdefault(repo, set()).update(info) return indicated_deps
def consolidate_dependencies(needs_ipython, child_program, requirement_files, manual_dependencies): """Parse files, get deps and merge them. Deps read later overwrite those read earlier.""" # We get the logger here because it's not defined at module level logger = logging.getLogger('fades') if needs_ipython: logger.debug("Adding ipython dependency because --ipython was detected") ipython_dep = parsing.parse_manual(['ipython']) else: ipython_dep = {} if child_program: srcfile_deps = parsing.parse_srcfile(child_program) logger.debug("Dependencies from source file: %s", srcfile_deps) docstring_deps = parsing.parse_docstring(child_program) logger.debug("Dependencies from docstrings: %s", docstring_deps) else: srcfile_deps = {} docstring_deps = {} all_dependencies = [ipython_dep, srcfile_deps, docstring_deps] if requirement_files is not None: for rf_path in requirement_files: rf_deps = parsing.parse_reqfile(rf_path) logger.debug('Dependencies from requirements file %r: %s', rf_path, rf_deps) all_dependencies.append(rf_deps) manual_deps = parsing.parse_manual(manual_dependencies) logger.debug("Dependencies from parameters: %s", manual_deps) all_dependencies.append(manual_deps) # Merge dependencies indicated_deps = {} for dep in all_dependencies: for repo, info in dep.items(): indicated_deps.setdefault(repo, set()).update(info) return indicated_deps
def go(argv): """Make the magic happen.""" parser = argparse.ArgumentParser( prog='PROG', epilog=help_epilog, usage=help_usage, formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument( '-V', '--version', action='store_true', help="show version and info about the system, and exit") parser.add_argument( '-v', '--verbose', action='store_true', help="send all internal debugging lines to stderr, which may be very " "useful to debug any problem that may arise.") parser.add_argument( '-q', '--quiet', action='store_true', help="don't show anything (unless it has a real problem), so the " "original script stderr is not polluted at all.") parser.add_argument( '-d', '--dependency', action='append', help="specify dependencies through command line (this option can be " "used multiple times)") parser.add_argument('-r', '--requirement', help="indicate from which file read the dependencies") parser.add_argument('-p', '--python', action='store', help=("Specify the Python interpreter to use.\n" " Default is: %s") % (sys.executable, )) parser.add_argument( '-x', '--exec', dest='executable', action='store_true', help=("Indicate that the child_program should be looked up in the " "virtualenv.")) parser.add_argument('-i', '--ipython', action='store_true', help="use IPython shell.") parser.add_argument('--system-site-packages', action='store_true', default=False, help=("Give the virtual environment access to the " "system site-packages dir.")) parser.add_argument( '--virtualenv-options', action='append', default=[], help=( "Extra options to be supplied to virtualenv. (this option can be " "used multiple times)")) parser.add_argument('--check-updates', action='store_true', help=("check for packages updates")) parser.add_argument( '--pip-options', action='append', default=[], help=("Extra options to be supplied to pip. (this option can be " "used multiple times)")) parser.add_argument('--rm', dest='remove', metavar='UUID', help=("Remove a virtualenv by UUID.")) parser.add_argument( '--clean-unused-venvs', action='store', help=("This option remove venvs that haven't been used for more than " "CLEAN_UNUSED_VENVS days. Appart from that, will compact usage " "stats file.\n" "When this option is present, the cleaning takes place at the " "beginning of the execution.")) parser.add_argument('child_program', nargs='?', default=None) parser.add_argument('child_options', nargs=argparse.REMAINDER) # support the case when executed from a shell-bang, where all the # parameters come in sys.argv[1] in a single string separated # by spaces (in this case, the third parameter is what is being # executed) if len(sys.argv) > 1 and " " in sys.argv[1]: real_args = sys.argv[1].split() + sys.argv[2:] cli_args = parser.parse_args(real_args) else: cli_args = parser.parse_args() # update args from config file (if needed). args = file_options.options_from_file(cli_args) # validate input, parameters, and support some special options if args.version: print("Running 'fades' version", fades.__version__) print(" Python:", sys.version_info) print(" System:", sys.platform) sys.exit(0) # set up logger and dump basic version info l = logger.set_up(args.verbose, args.quiet) l.debug("Running Python %s on %r", sys.version_info, sys.platform) l.debug("Starting fades v. %s", fades.__version__) l.debug("Arguments: %s", args) # verify that the module is NOT being used from a virtualenv if detect_inside_virtualenv(sys.prefix, getattr(sys, 'real_prefix', None), getattr(sys, 'base_prefix', None)): l.warning( "fades is running from a virtualenv (%r), which is not supported", sys.prefix) if args.verbose and args.quiet: l.warning("Overriding 'quiet' option ('verbose' also requested)") # start the virtualenvs manager venvscache = cache.VEnvsCache( os.path.join(helpers.get_basedir(), 'venvs.idx')) # start usage manager usage_manager = envbuilder.UsageManager( os.path.join(helpers.get_basedir(), 'usage_stats'), venvscache) rc = 0 if args.clean_unused_venvs: try: max_days_to_keep = int(args.clean_unused_venvs) usage_manager.clean_unused_venvs(max_days_to_keep) except: rc = 1 l.debug("CLEAN_UNUSED_VENVS must be an integer.") raise finally: sys.exit(rc) uuid = args.remove if uuid: venv_data = venvscache.get_venv(uuid=uuid) if venv_data: # remove this venv from the cache env_path = venv_data.get('env_path') if env_path: envbuilder.destroy_venv(env_path, venvscache) else: l.warning( "Invalid 'env_path' found in virtualenv metadata: %r. " "Not removing virtualenv.", env_path) else: l.warning('No virtualenv found with uuid: %s.', uuid) return # parse file and get deps if args.ipython: l.debug("Adding ipython dependency because --ipython was detected") ipython_dep = parsing.parse_manual(['ipython']) else: ipython_dep = {} if args.executable: indicated_deps = {} docstring_deps = {} else: indicated_deps = parsing.parse_srcfile(args.child_program) l.debug("Dependencies from source file: %s", indicated_deps) docstring_deps = parsing.parse_docstring(args.child_program) l.debug("Dependencies from docstrings: %s", docstring_deps) reqfile_deps = parsing.parse_reqfile(args.requirement) l.debug("Dependencies from requirements file: %s", reqfile_deps) manual_deps = parsing.parse_manual(args.dependency) l.debug("Dependencies from parameters: %s", manual_deps) indicated_deps = _merge_deps(ipython_dep, indicated_deps, docstring_deps, reqfile_deps, manual_deps) # Check for packages updates if args.check_updates: helpers.check_pypi_updates(indicated_deps) # get the interpreter version requested for the child_program interpreter, is_current = helpers.get_interpreter_version(args.python) # options pip_options = args.pip_options # pip_options mustn't store. options = {} options['pyvenv_options'] = [] options['virtualenv_options'] = args.virtualenv_options if args.system_site_packages: options['virtualenv_options'].append("--system-site-packages") options['pyvenv_options'] = ["--system-site-packages"] create_venv = False venv_data = venvscache.get_venv(indicated_deps, interpreter, uuid, options) if venv_data: env_path = venv_data['env_path'] # A venv was found in the cache check if its valid or re-generate it. if not os.path.exists(env_path): l.warning( "Missing directory (the virtualenv will be re-created): %r", env_path) venvscache.remove(env_path) create_venv = True else: create_venv = True if create_venv: # Create a new venv venv_data, installed = envbuilder.create_venv(indicated_deps, args.python, is_current, options, pip_options) # store this new venv in the cache venvscache.store(installed, venv_data, interpreter, options) # run forest run!! python_exe = 'ipython' if args.ipython else 'python' python_exe = os.path.join(venv_data['env_bin_path'], python_exe) # store usage information usage_manager.store_usage_stat(venv_data, venvscache) if args.child_program is None: interactive = True l.debug("Calling the interactive Python interpreter") p = subprocess.Popen([python_exe]) else: interactive = False if args.executable: cmd = [os.path.join(venv_data['env_bin_path'], args.child_program)] else: cmd = [python_exe, args.child_program] l.debug("Calling the child program %r with options %s", args.child_program, args.child_options) p = subprocess.Popen(cmd + args.child_options) def _signal_handler(signum, _): """Handle signals received by parent process, send them to child. The only exception is CTRL-C, that is generated *from* the interactive interpreter (it's a keyboard combination!), so we swallow it for the interpreter to not see it twice. """ if interactive and signum == signal.SIGINT: l.debug("Swallowing signal %s", signum) else: l.debug("Redirecting signal %s to child", signum) os.kill(p.pid, signum) # redirect the useful signals for s in REDIRECTED_SIGNALS: signal.signal(s, _signal_handler) # wait child to finish, end rc = p.wait() if rc: l.debug("Child process not finished correctly: returncode=%d", rc) sys.exit(rc)
def go(argv): """Make the magic happen.""" parser = argparse.ArgumentParser(prog='PROG', epilog=help_epilog, usage=help_usage, formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument('-V', '--version', action='store_true', help="show version and info about the system, and exit") parser.add_argument('-v', '--verbose', action='store_true', help="send all internal debugging lines to stderr, which may be very " "useful to debug any problem that may arise.") parser.add_argument('-q', '--quiet', action='store_true', help="don't show anything (unless it has a real problem), so the " "original script stderr is not polluted at all.") parser.add_argument('-d', '--dependency', action='append', help="specify dependencies through command line (this option can be " "used multiple times)") parser.add_argument('-r', '--requirement', help="indicate from which file read the dependencies") parser.add_argument('-p', '--python', action='store', help=("Specify the Python interpreter to use.\n" " Default is: %s") % (sys.executable,)) parser.add_argument('-x', '--exec', dest='executable', action='store_true', help=("Indicate that the child_program should be looked up in the " "virtualenv.")) parser.add_argument('-i', '--ipython', action='store_true', help="use IPython shell.") parser.add_argument('--system-site-packages', action='store_true', default=False, help=("Give the virtual environment access to the " "system site-packages dir.")) parser.add_argument('--virtualenv-options', action='append', default=[], help=("Extra options to be supplied to virtualenv. (this option can be " "used multiple times)")) parser.add_argument('--check-updates', action='store_true', help=("check for packages updates")) parser.add_argument('--pip-options', action='append', default=[], help=("Extra options to be supplied to pip. (this option can be " "used multiple times)")) parser.add_argument('--rm', dest='remove', metavar='UUID', help=("Remove a virtualenv by UUID.")) parser.add_argument('--clean-unused-venvs', action='store', help=("This option remove venvs that haven't been used for more than " "CLEAN_UNUSED_VENVS days. Appart from that, will compact usage " "stats file.\n" "When this option is present, the cleaning takes place at the " "beginning of the execution.")) parser.add_argument('child_program', nargs='?', default=None) parser.add_argument('child_options', nargs=argparse.REMAINDER) # support the case when executed from a shell-bang, where all the # parameters come in sys.argv[1] in a single string separated # by spaces (in this case, the third parameter is what is being # executed) if len(sys.argv) > 1 and " " in sys.argv[1]: real_args = sys.argv[1].split() + sys.argv[2:] cli_args = parser.parse_args(real_args) else: cli_args = parser.parse_args() # update args from config file (if needed). args = file_options.options_from_file(cli_args) # validate input, parameters, and support some special options if args.version: print("Running 'fades' version", fades.__version__) print(" Python:", sys.version_info) print(" System:", sys.platform) sys.exit() # set up logger and dump basic version info l = logger.set_up(args.verbose, args.quiet) l.debug("Running Python %s on %r", sys.version_info, sys.platform) l.debug("Starting fades v. %s", fades.__version__) l.debug("Arguments: %s", args) if args.verbose and args.quiet: l.warning("Overriding 'quiet' option ('verbose' also requested)") # start the virtualenvs manager venvscache = cache.VEnvsCache(os.path.join(helpers.get_basedir(), 'venvs.idx')) # start usage manager usage_manager = envbuilder.UsageManager(os.path.join(helpers.get_basedir(), 'usage_stats'), venvscache) if args.clean_unused_venvs: try: max_days_to_keep = int(args.clean_unused_venvs) usage_manager.clean_unused_venvs(max_days_to_keep) except: l.debug("CLEAN_UNUSED_VENVS must be an integer.") raise finally: sys.exit() uuid = args.remove if uuid: venv_data = venvscache.get_venv(uuid=uuid) if venv_data: # remove this venv from the cache env_path = venv_data.get('env_path') if env_path: envbuilder.destroy_venv(env_path, venvscache) else: l.warning("Invalid 'env_path' found in virtualenv metadata: %r. " "Not removing virtualenv.", env_path) else: l.warning('No virtualenv found with uuid: %s.', uuid) return # parse file and get deps if args.ipython: l.debug("Adding ipython dependency because --ipython was detected") ipython_dep = parsing.parse_manual(['ipython']) else: ipython_dep = {} if args.executable: indicated_deps = {} docstring_deps = {} else: indicated_deps = parsing.parse_srcfile(args.child_program) l.debug("Dependencies from source file: %s", indicated_deps) docstring_deps = parsing.parse_docstring(args.child_program) l.debug("Dependencies from docstrings: %s", docstring_deps) reqfile_deps = parsing.parse_reqfile(args.requirement) l.debug("Dependencies from requirements file: %s", reqfile_deps) manual_deps = parsing.parse_manual(args.dependency) l.debug("Dependencies from parameters: %s", manual_deps) indicated_deps = _merge_deps(ipython_dep, indicated_deps, docstring_deps, reqfile_deps, manual_deps) # Check for packages updates if args.check_updates: helpers.check_pypi_updates(indicated_deps) # get the interpreter version requested for the child_program interpreter, is_current = helpers.get_interpreter_version(args.python) # options pip_options = args.pip_options # pip_options mustn't store. options = {} options['pyvenv_options'] = [] options['virtualenv_options'] = args.virtualenv_options if args.system_site_packages: options['virtualenv_options'].append("--system-site-packages") options['pyvenv_options'] = ["--system-site-packages"] create_venv = False venv_data = venvscache.get_venv(indicated_deps, interpreter, uuid, options) if venv_data: env_path = venv_data['env_path'] # A venv was found in the cache check if its valid or re-generate it. if not os.path.exists(env_path): l.warning("Missing directory (the virtualenv will be re-created): %r", env_path) venvscache.remove(env_path) create_venv = True else: create_venv = True if create_venv: # Create a new venv venv_data, installed = envbuilder.create_venv(indicated_deps, args.python, is_current, options, pip_options) # store this new venv in the cache venvscache.store(installed, venv_data, interpreter, options) # run forest run!! python_exe = 'ipython' if args.ipython else 'python' python_exe = os.path.join(venv_data['env_bin_path'], python_exe) # store usage information usage_manager.store_usage_stat(venv_data, venvscache) if args.child_program is None: l.debug("Calling the interactive Python interpreter") p = subprocess.Popen([python_exe]) else: if args.executable: cmd = [os.path.join(venv_data['env_bin_path'], args.child_program)] else: cmd = [python_exe, args.child_program] l.debug("Calling the child program %r with options %s", args.child_program, args.child_options) p = subprocess.Popen(cmd + args.child_options) def _signal_handler(signum, _): """Handle signals received by parent process, send them to child.""" l.debug("Redirecting signal %s to child", signum) os.kill(p.pid, signum) # redirect these signals for s in REDIRECTED_SIGNALS: signal.signal(s, _signal_handler) # wait child to finish, end rc = p.wait() if rc: l.debug("Child process not finished correctly: returncode=%d", rc) sys.exit(rc)