def check_requirements(): if not os.path.exists(requirements): sys.exit( ansi.error() + ' %s is missing. Please check it in.' % ansi.underline(requirements) ) with open(requirements, 'r', encoding='utf-8') as f: dependencies = f.readlines() vcs = [d for d in dependencies if re.match(r'^(-e )?(git|svn|hg|bzr).*', d)] dependencies = list(set(dependencies) - set(vcs)) missing = [] try: pkg_resources.require(dependencies) except (pkg_resources.ContextualVersionConflict, DistributionNotFound, VersionConflict) as error: missing.append(str(error)) except pkg_resources.RequirementParseError: pass if missing: missing = ' missing requirement:\n ' + os.linesep.join(missing) if '--env-checked' in sys.argv: sys.exit(ansi.error() + missing + '\nRequirement installation failure, please check for errors in:\n $ lore install\n') else: print(ansi.warning() + missing) import lore.__main__ lore.__main__.install_requirements(None) reboot('--env-checked') else: return True
def validate(): """Display error messages and exit if no lore environment can be found. """ if not os.path.exists(os.path.join(ROOT, APP, '__init__.py')): message = ansi.error() + ' Python module not found.' if os.environ.get('LORE_APP') is None: message += ' $LORE_APP is not set. Should it be different than "%s"?' % APP else: message += ' $LORE_APP is set to "%s". Should it be different?' % APP sys.exit(message) if exists(): return if len(sys.argv) > 1: command = sys.argv[1] else: command = 'lore' sys.exit( ansi.error() + ' %s is only available in lore ' 'app directories (missing %s)' % ( ansi.bold(command), ansi.underline(VERSION_PATH) ) )
def _get_fully_qualified_class(name): module, klass = name.rsplit('.', 1) try: module = importlib.import_module(module) except env.ModuleNotFoundError as ex: sys.exit(ansi.error() + ' "%s" does not exist in this directoy! Are you sure you typed the name correctly?' % module) try: value = getattr(module, klass) except AttributeError as ex: sys.exit(ansi.error() + ' "%s" does not exist in %s! Are you sure you typed the fully qualified module.Class name correctly?' % (klass, module)) return value
def install_python_version(): if env.launched(): return if not env.PYTHON_VERSION: env.set_python_version('.'.join(sys.version_info)) print(ansi.warning() + ' %s does not exist. Creating with %s' % (env.VERSION_PATH, env.PYTHON_VERSION)) with open(env.VERSION_PATH, 'w', encoding='utf-8') as f: f.write(env.PYTHON_VERSION + os.linesep) if platform.system() == 'Windows': print(ansi.warning() + ' Lore only uses the installed python version on Windows.') else: if not env.PYENV: sys.exit( ansi.error() + ' pyenv is not installed. Lore is broken. try:\n' ' $ pip uninstall lore && pip install lore\n' ) versions = subprocess.check_output( (env.BIN_PYENV, 'versions', '--bare') ).decode('utf-8').split(os.linesep) if env.PYTHON_VERSION not in versions: print(ansi.success('INSTALL') + ' python %s' % env.PYTHON_VERSION) if platform.system() == 'Darwin': install_xcode() subprocess.check_call(('git', '-C', env.PYENV, 'pull')) subprocess.check_call((env.BIN_PYENV, 'install', env.PYTHON_VERSION)) subprocess.check_call((env.BIN_PYENV, 'rehash'))
def init(parsed, unknown): template = os.path.join(os.path.dirname(__file__), 'template', 'init') if os.path.exists(parsed.name): sys.exit( ansi.error() + ' "' + parsed.name + '" already exists in this directoy! Lore can not create a new project with this name.' ) shutil.copytree(template, parsed.name, symlinks=False, ignore=None) os.chdir(parsed.name) shutil.move('app', parsed.name) requirements = 'lore' if unknown: requirements += '[' + ','.join([r[2:] for r in unknown]) + ']' with open('requirements.txt', 'wt') as file: file.write(requirements) python_version = parsed.python_version or '3.6.4' with open('runtime.txt', 'wt') as file: file.write('python-' + python_version + '\n') reload(lore.env) install(parsed, unknown)
def install_tensorflow(): description = subprocess.check_output( (env.bin_python, '-m', 'pip', 'show', 'tensorflow')).decode('utf-8') version = re.match('.*^Version: ([^\n]+)', description, re.S | re.M).group(1) if not version: sys.exit(ansi.error() + ' tensorflow is not in requirements.txt') print(ansi.success('NATIVE') + ' tensorflow ' + version) python_version = ''.join(env.python_version.split('.')[0:2]) cached = os.path.join( env.pyenv, 'cache', 'tensorflow_pkg', 'tensorflow-' + version + '-cp' + python_version + '*') paths = glob.glob(cached) if not paths: build_tensorflow(version) paths = glob.glob(cached) path = paths[0] subprocess.check_call( (env.bin_python, '-m', 'pip', 'uninstall', '-y', 'tensorflow')) print(ansi.success('INSTALL') + ' tensorflow native build') subprocess.check_call((env.bin_python, '-m', 'pip', 'install', path))
def install_pyenv(): virtualenv = os.path.join(env.PYENV, 'plugins', 'pyenv-virtualenv') if os.path.exists(env.BIN_PYENV) and os.path.exists(virtualenv): return if os.path.exists(env.PYENV) and not os.path.isfile(env.BIN_PYENV): print(ansi.warning() + ' pyenv executable is not present at %s' % env.BIN_PYENV) while True: answer = input( 'Would you like to blow away ~/.pyenv and rebuild from scratch? [Y/n] ' ) if answer in ['', 'y', 'Y']: shutil.rmtree(lore.env.PYENV) break elif answer in ['n', 'N']: sys.exit(ansi.error() + ' please fix pyenv before continuing') else: print('please enter Y or N') if not os.path.exists(env.PYENV): print(ansi.success('INSTALLING') + ' pyenv') subprocess.check_call( ('git', 'clone', 'https://github.com/pyenv/pyenv.git', env.PYENV)) else: print(ansi.success('CHECK') + ' existing pyenv installation') env.set_python_version(env.PYTHON_VERSION) if not os.path.exists(virtualenv): print(ansi.success('INSTALLING') + ' pyenv virtualenv') subprocess.check_call( ('git', 'clone', 'https://github.com/pyenv/pyenv-virtualenv.git', virtualenv)) else: print(ansi.success('CHECK') + ' existing virtualenv installation')
def install_python_version(): if env.launched(): return if not env.python_version: env.set_python_version('.'.join(sys.version_info)) print(ansi.warning() + ' %s does not exist. Creating with %s' % (env.version_path, env.python_version)) with open(env.version_path, 'w', encoding='utf-8') as f: f.write(env.python_version + os.linesep) if not env.pyenv: sys.exit(ansi.error() + ' pyenv is not installed. Lore is broken. try:\n' ' $ pip uninstall lore && pip install lore\n') versions = subprocess.check_output( (env.bin_pyenv, 'versions', '--bare')).decode('utf-8').split(os.linesep) if env.python_version not in versions: print(ansi.success('INSTALL') + ' python %s' % env.python_version) if platform.system() == 'Darwin': install_xcode() subprocess.check_call(('git', '-C', env.pyenv, 'pull')) subprocess.check_call((env.bin_pyenv, 'install', env.python_version)) subprocess.check_call((env.bin_pyenv, 'rehash'))
def validate(): if not os.path.exists(os.path.join(root, project, '__init__.py')): sys.exit( ansi.error() + ' Python module not found. Do you need to change $LORE_PROJECT from "%s"?' % project) if exists(): return if len(sys.argv) > 1: command = sys.argv[1] else: command = 'lore' sys.exit(ansi.error() + ' %s is only available in lore ' 'project directories (missing %s)' % (ansi.bold(command), ansi.underline(version_path)))
def fit(parsed, unknown): print(ansi.success('FITTING ') + parsed.model) Model = _get_fully_qualified_class(parsed.model) model = Model() valid_model_fit_args = _get_valid_fit_args(model.fit) valid_estimator_fit_args = _get_valid_fit_args(model.estimator.fit) valid_fit_args = valid_model_fit_args.args[1:] + valid_estimator_fit_args.args[1:] model_attrs = _filter_private_attributes(model.__dict__) pipeline_attrs = _filter_private_attributes(model.pipeline.__dict__) estimator_attrs = _filter_private_attributes(model.estimator.__dict__) estimator_attrs.pop('model', None) grouped, unpaired = _pair_args(unknown) # assign args to their receivers fit_args = {} unknown_args = [] for name, value in grouped: if name in model_attrs: value = _cast_attr(value, getattr(model, name)) setattr(model, name, value) elif name in pipeline_attrs: value = _cast_attr(value, getattr(model.pipeline, name)) setattr(model.pipeline, name, value) elif name in estimator_attrs: value = _cast_attr(value, getattr(model.estimator, name)) setattr(model.estimator, name, value) elif name in valid_model_fit_args.args: index = valid_model_fit_args.args.index(name) from_end = index - len(valid_model_fit_args.args) default = None if from_end < len(valid_model_fit_args.defaults): default = valid_model_fit_args.defaults[from_end] fit_args[name] = _cast_attr(value, default) elif name in valid_estimator_fit_args.args: index = valid_estimator_fit_args.args.index(name) from_end = index - len(valid_estimator_fit_args.args) default = None if from_end < len(valid_estimator_fit_args.defaults): default = valid_estimator_fit_args.defaults[from_end] fit_args[name] = _cast_attr(value, default) else: unknown_args.append(name) unknown_args += unpaired if unknown_args: msg = ansi.bold("Valid model attributes") + ": %s\n" % ', '.join(sorted(model_attrs.keys())) msg += ansi.bold("Valid estimator attributes") + ": %s\n" % ', '.join(sorted(estimator_attrs.keys())) msg += ansi.bold("Valid pipeline attributes") + ": %s\n" % ', '.join(sorted(pipeline_attrs.keys())) msg += ansi.bold("Valid fit arguments") + ": %s\n" % ', '.join(sorted(valid_fit_args)) sys.exit(ansi.error() + ' Unknown arguments: %s\n%s' % (unknown_args, msg)) model.fit(score=parsed.score, test=parsed.test, **fit_args) print(ansi.success() + ' Fitting: %i\n%s' % (model.fitting, json.dumps(model.stats, indent=2)))
def install_requirements(args): source = env.REQUIREMENTS if not os.path.exists(source): sys.exit(ansi.error() + ' %s is missing. You should check it in to version ' 'control.' % ansi.underline(source)) pip_install(source, args) freeze_requirements()
def execute(parsed, unknown): if len(unknown) == 0: print(ansi.error() + ' no args to execute!') return print(ansi.success('EXECUTE ') + ' '.join(unknown)) os.environ['PATH'] = os.path.join(env.PREFIX, 'bin') + ':' + os.environ['PATH'] subprocess.check_call(unknown, env=os.environ)
def reboot(*args): args = list(sys.argv) + list(args) if args[0] == 'python' or not args[0]: args[0] = bin_python elif os.path.basename(sys.argv[0]) in ['lore', 'lore.exe']: args[0] = bin_lore try: os.execv(args[0], args) except Exception as e: if args[0] == bin_lore and args[1] == 'console': print(ansi.error() + ' Your jupyter kernel may be corrupt. Please remove it so lore can reinstall:\n $ rm ' + jupyter_kernel_path) raise e
def check_version(): if sys.version_info[0:3] == python_version_info[0:3]: return sys.exit( ansi.error() + ' your virtual env points to the wrong python version. ' 'This is likely because you used a python installer that clobbered ' 'the system installation, which breaks virtualenv creation. ' 'To fix, check this symlink, and delete the installation of python ' 'that it is brokenly pointing to, then delete the virtual env itself ' 'and rerun lore install: ' + os.linesep + os.linesep + bin_python + os.linesep)
def _get_model(name): module, klass = name.rsplit('.', 1) try: module = importlib.import_module(module) Model = getattr(module, klass) except (AttributeError, ModuleNotFoundError): sys.exit( ansi.error() + ' "' + name + '" does not exist in this directoy! Are you sure you typed the fully qualified module.Class name correctly?' ) return Model
def reboot(*args): """Reboot python in the Lore virtualenv """ args = list(sys.argv) + list(args) if args[0] == 'python' or not args[0]: args[0] = BIN_PYTHON elif os.path.basename(sys.argv[0]) in ['lore', 'lore.exe']: args[0] = BIN_LORE try: os.execv(args[0], args) except Exception as e: if args[0] == BIN_LORE and args[1] == 'console' and JUPYTER_KERNEL_PATH: print(ansi.error() + ' Your jupyter kernel may be corrupt. Please remove it so lore can reinstall:\n $ rm ' + JUPYTER_KERNEL_PATH) raise e
def check_version(): """Sanity check version information for corrupt virtualenv symlinks """ if sys.version_info[0:3] == PYTHON_VERSION_INFO[0:3]: return sys.exit( ansi.error() + ' your virtual env points to the wrong python version. ' 'This is likely because you used a python installer that clobbered ' 'the system installation, which breaks virtualenv creation. ' 'To fix, check this symlink, and delete the installation of python ' 'that it is brokenly pointing to, then delete the virtual env itself ' 'and rerun lore install: ' + os.linesep + os.linesep + BIN_PYTHON + os.linesep)
def pip_install(path, args): if not os.path.exists(path): return pip_args = [env.bin_python, '-m', 'pip', 'install', '-r', path] if hasattr(args, 'upgrade') and args.upgrade: pip_args += ['--upgrade', '--upgrade-strategy=eager'] print(ansi.success('EXECUTE ') + ' '.join(pip_args)) try: subprocess.check_call(pip_args) except subprocess.CalledProcessError: sys.exit(ansi.error() + ' could not:\n $ lore pip install -r %s\nPlease try ' 'installing failed packages manually, or upgrade failed ' 'packages with:\n $ lore install --upgrade ' % path)
def task(parsed, unknown): if len(parsed.task) == 0: tasks = [] for module_finder, module_name, _ in pkgutil.iter_modules( [lore.env.APP + '/' + 'tasks']): module = importlib.import_module(lore.env.APP + '.tasks.' + module_name) for class_name, member in inspect.getmembers(module): if inspect.isclass(member) and issubclass( member, lore.tasks.base.Base) and hasattr( member, 'main'): tasks.append(member) sys.exit('\n%s Tasks\n%s\n %s\n' % (lore.env.APP, '-' * (6 + len(lore.env.APP)), '\n '.join( '%s.%s: %s' % (task.__module__, task.__name__, task.main.__doc__) for task in tasks))) for task in parsed.task: task_class = _get_fully_qualified_class(task) instance = task_class() grouped, unpaired = _pair_args(unknown) argspec = _get_valid_fit_args(instance.main) defaults = [None] * (len(argspec.args) - len(argspec.defaults)) + list( argspec.defaults) valid_args = dict(zip(argspec.args, defaults)) valid_args.pop('self', None) args = dict(grouped) unknown_args = [] cast_args = {} for name, value in args.items(): if name in valid_args: cast_args[name] = _cast_attr(value, valid_args[name]) else: unknown_args.append(name) unknown_args += unpaired if unknown_args: msg = ansi.bold("Valid task arguments") + ": \n%s\n" % "\n".join( ' %s=%s' % i for i in valid_args.items()) sys.exit(ansi.error() + ' Unknown arguments: %s\n%s\n%s' % (unknown_args, msg, instance.main.__doc__)) with timer('execute %s' % task): print(ansi.success('RUNNING ') + task) logger.info('starting task: %s %s' % (task, args)) instance.main(**cast_args)
def api(parsed, unknown): api_path = os.path.join(env.ROOT, env.APP, 'api') endpoint_paths = [] consumer_paths = [] if 'HUB_LISTENERS' in os.environ: for name in os.environ.get('HUB_LISTENERS').split(','): endpoint = os.path.join(api_path, name + '_endpoint.py') consumer = os.path.join(api_path, name + '_consumer.py') if os.path.exists(endpoint): endpoint_paths.append(endpoint) elif os.path.exists(consumer): consumer_paths.append(consumer) else: raise IOError('No file found for listener "%s". The following paths were checked:\n %s\n %s' % ( name, consumer, endpoint)) else: endpoint_paths = glob.glob(os.path.join(api_path, '*_endpoint.py')) consumer_paths = glob.glob(os.path.join(api_path, '*_consumer.py')) for path in endpoint_paths + consumer_paths: module = os.path.basename(path)[:-3] if sys.version_info.major == 2: import imp imp.load_source(module, path) elif sys.version_info.major == 3: import importlib.util spec = importlib.util.spec_from_file_location(module, path) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) util.strip_one_off_handlers() if len(endpoint_paths) > 0 and len(consumer_paths) > 0: from hub.listeners.combined import CombinedListener as Listener elif len(endpoint_paths) > 0: from hub.listeners.endpoint import EndpointListener as Listener elif len(consumer_paths) > 0: from hub.listeners.consumer import ConsumerListener as Listener else: raise IOError('No hub listeners found in %s' % api_path) try: Listener( os.environ.get('HUB_APP_NAME', env.APP), concurrency=os.environ.get("HUB_CONCURRENCY", 4), host_index=os.environ.get("RABBIT_HOST_INDEX") ).start() except KeyboardInterrupt: exit(ansi.error('INTERRUPT') + ' Shutting down...')
def launch(): if launched(): check_version() os.chdir(root) return if not os.path.exists(bin_lore): missing = ' %s virtualenv is missing.' % project if '--launched' in sys.argv: sys.exit(ansi.error() + missing + ' Please check for errors during:\n $ lore install\n') else: print(ansi.warning() + missing) import lore.__main__ lore.__main__.install(None, None) reboot('--env-launched')
def install_pyenv(): home = os.environ.get('HOME') if not home: return pyenv = os.path.join(home, '.pyenv') bin_pyenv = os.path.join(pyenv, 'bin', 'pyenv') virtualenv = os.path.join(pyenv, 'plugins', 'pyenv-virtualenv') if os.path.exists(bin_pyenv) and os.path.exists(virtualenv): return if os.path.exists(pyenv) and not os.path.isfile(bin_pyenv): print(ansi.warning() + ' pyenv executable is not present at %s' % bin_pyenv) while True: answer = input('Would you like to blow away ~/.pyenv and rebuild from scratch? [Y/n] ') if answer in ['', 'y', 'Y']: shutil.rmtree(pyenv) break elif answer in ['n', 'N']: sys.exit(ansi.error() + ' please fix pyenv before continuing') else: print('please enter Y or N') if not os.path.exists(pyenv): print(ansi.success('INSTALLING') + ' pyenv') subprocess.check_call(( 'git', 'clone', 'https://github.com/pyenv/pyenv.git', pyenv )) else: print(ansi.success('CHECK') + ' existing pyenv installation') env.pyenv = pyenv env.bin_pyenv = bin_pyenv env.set_python_version(env.python_version) if not os.path.exists(virtualenv): print(ansi.success('INSTALLING') + ' pyenv virtualenv') subprocess.check_call(( 'git', 'clone', 'https://github.com/pyenv/pyenv-virtualenv.git', virtualenv )) else: print(ansi.success('CHECK') + ' existing virtualenv installation')
def init(parsed, unknown): root = os.path.normpath(os.path.realpath(parsed.name)) name = os.path.basename(root) if os.path.exists(root): print(ansi.info() + ' converting existing directory to a Lore App') else: print(ansi.info() + ' creating new Lore App!') if parsed.bare: if not os.path.exists(root): os.makedirs(root) else: template = os.path.join(os.path.dirname(__file__), 'template', 'init') if os.path.exists(root): sys.exit( ansi.error() + ' "' + parsed.name + '" already exists in this directoy! Add --bare to avoid clobbering existing files.' ) shutil.copytree(template, root, symlinks=False, ignore=None) shutil.move(os.path.join(root, 'app'), os.path.join(root, name)) os.chdir(root) with open('requirements.txt', 'a+') as file: file.seek(0) lines = file.readlines() if next((line for line in lines if re.match(r'^lore[!<>=]', line)), None) is None: file.write('lore' + os.linesep) python_version = parsed.python_version or lore.env.read_version( 'runtime.txt') or '3.6.6' open('runtime.txt', 'w').write('python-' + python_version + '\n') module = os.path.join(root, name, '__init__.py') if not os.path.exists(os.path.dirname(module)): os.makedirs(os.path.dirname(module)) with open(module, 'a+') as file: file.seek(0) lines = file.readlines() if next((line for line in lines if re.match(r'\bimport lore\b', line)), None) is None: file.write('import lore' + os.linesep) lore.env.reload(lore.env) install(parsed, unknown)
def launch(): """Ensure that python is running from the Lore virtualenv past this point. """ if launched(): check_version() os.chdir(ROOT) return if not os.path.exists(BIN_LORE): missing = ' %s virtualenv is missing.' % APP if '--launched' in sys.argv: sys.exit(ansi.error() + missing + ' Please check for errors during:\n $ lore install\n') else: print(ansi.warning() + missing) import lore.__main__ lore.__main__.install(None, None) reboot('--env-launched')
def _generate_template(type, parsed, **kwargs): env.require(lore.dependencies.INFLECTION) import inflection name = parsed.name kwargs = kwargs or {} for attr in ['keras', 'xgboost', 'sklearn']: if hasattr(parsed, attr): kwargs[attr] = getattr(parsed, attr) kwargs['major_version'] = sys.version_info[0] kwargs['full_version'] = env.PYTHON_VERSION notebooks = ['features', 'architecture'] name = inflection.underscore(name) if type == 'notebooks': for notebook in notebooks: _generate_template(notebook, parsed, **kwargs) return if type == 'test': destination = os.path.join(inflection.pluralize(type), 'unit', 'test_' + name + '.py') elif type in notebooks: destination = os.path.join('notebooks', name, type + '.ipynb') else: destination = os.path.join(env.APP, inflection.pluralize(type), name + '.py') if os.path.exists(destination): sys.exit(ansi.error() + ' %s already exists' % destination) dir = os.path.dirname(destination) if not os.path.exists(dir): os.makedirs(dir) if type not in notebooks: open(os.path.join(dir, '__init__.py'), 'w') kwargs['app_name'] = env.APP kwargs['module_name'] = name kwargs['class_name'] = inflection.camelize(name) code = _render_template(type + '.py.j2', **kwargs) with open(destination, 'w+') as file: file.write(code) print(ansi.success('CREATED ') + destination)
def _generate_template(type, parsed, **kwargs): import inflection name = parsed.name kwargs = kwargs or {} kwargs['keras'] = parsed.keras kwargs['xgboost'] = parsed.xgboost kwargs['sklearn'] = parsed.sklearn kwargs['major_version'] = sys.version_info[0] kwargs['full_version'] = lore.env.python_version notebooks = ['features', 'architecture'] name = inflection.underscore(name) if type == 'notebooks': for notebook in notebooks: _generate_template(notebook, parsed, **kwargs) return if type == 'test': destination = os.path.join(inflection.pluralize(type), 'unit', 'test_' + name + '.py') elif type in notebooks: destination = os.path.join('notebooks', name, type + '.ipynb') else: destination = os.path.join(lore.env.project, inflection.pluralize(type), name + '.py') if os.path.exists(destination): sys.exit(ansi.error() + ' %s already exists' % destination) dir = os.path.dirname(destination) if not os.path.exists(dir): os.makedirs(dir) if type not in notebooks: open(os.path.join(dir, '__init__.py'), 'w') kwargs['app_name'] = lore.env.project kwargs['module_name'] = name kwargs['class_name'] = inflection.camelize(name) code = _render_template(type + '.py.j2', **kwargs) with open(destination, 'w+') as file: file.write(code) print(ansi.success('CREATED ') + destination)
def generate_pipeline(parsed, unknown): if not parsed.holdout: sys.exit(ansi.error() + ' unknown pipeline type; try --holdout') _generate_template('pipeline', parsed)