Coverage for pylucid/pylucid_boot.py : 54%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
#!/usr/bin/python3
pylucid bootstrap ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A interactive shell for booting the 'pylucid' project.
Note: - This file is "self contained". - It used **only** stuff from Python lib. - So it's "run able" on a bare python 3 installation - On debian / ubuntu the 'python3-venv' package is needed!
usage, e.g.:
$ wget https://raw.githubusercontent.com/jedie/PyLucid/master/pylucid/boot_pylucid.py $ python3 boot_pylucid.py
pylucid_boot> boot ~/pylucid-env
NOTE: * This file is generated via cookiecutter! * Don't edit it directly!
* The source file can be found here: https://github.com/jedie/bootstrap_env/blob/master/bootstrap_env/boot_source/
* Create issues about this file here: https://github.com/jedie/bootstrap_env/issues
* Pull requests are welcome ;)
:created: 11.03.2018 by Jens Diemer, www.jensdiemer.de :copyleft: 2018 by the bootstrap_env team, see AUTHORS for more details. :license: GNU General Public License v3 or later (GPLv3+), see LICENSE for more details. """
print("\nERROR: Python 3.5 or greater is required!") print("(Current Python Verison is %s)\n" % sys.version.split(" ",1)[0]) sys.exit(101)
except ImportError as err: # e.g.: debian / ubuntu doesn't have venv installed, isn't it?!? print("\nERROR: 'venv' not available: %s (Maybe 'python3-venv' package not installed?!?)" % err)
except ImportError as err: # e.g.: debian / ubuntu doesn't have venv installed, isn't it?!? print("\nERROR: 'ensurepip' not available: %s (Maybe 'python3-venv' package not installed?!?)" % err)
# admin shell console script entry point name ('setup.py # (used to call 'upgrade_requirements' after virtualenv creation) # It's the 'scripts' keyword argument in project 'setup.py' # see: # https://python-packaging.readthedocs.io/en/latest/command-line-scripts.html#the-scripts-keyword-argument #
# Note: # on 'master' branch: '--pre' flag must not be set: So the last release on PyPi will be installed. # on 'develop' branch: set the '--pre' flag and publish 'preview' versions on PyPi. # PACKAGE_NAME ]
# print("SELF_FILE_PATH: %s" % SELF_FILE_PATH) # print("ROOT_PATH: %s" % ROOT_PATH) # print("OWN_FILE_NAME: %s" % OWN_FILE_NAME)
# Maybe this is not the best way?!?
else: print("We are not in a virtualenv, ok.")
""" Borrowed from Django: https://github.com/django/django/blob/master/django/utils/termcolors.py
>>> c = Colorizer() >>> c.supports_colors() True >>> c.color_support = True >>> c.colorize('no color') 'no color' >>> c.colorize('bold', opts=("bold",)) '\\x1b[1mbold\\x1b[0m' >>> c.colorize("colors!", foreground="red", background="blue", opts=("bold", "blink")) '\\x1b[31;44;1;5mcolors!\\x1b[0m' """
return False
# isatty is not always implemented! return True else:
""" Returns your text, enclosed in ANSI graphics codes. """
code_list = []
if foreground: code_list.append(self._foreground_colors[foreground]) if background: code_list.append(self._background_colors[background])
for option in opts: code_list.append(self._opt_dict[option])
if not code_list: return text
return "\x1b[%sm%s\x1b[0m" % (';'.join(code_list), text)
""" colorize and print to stdout """
""" colorize and print to stderr """
for background_color in sorted(self._background_colors.keys()): line = ["%10s:" % background_color] for foreground_color in sorted(self._foreground_colors.keys()): line.append( self.colorize(" %s " % foreground_color, foreground=foreground_color, background=background_color ) )
for opt in sorted(self._opt_dict.keys()): line.append( self.colorize(" %s " % opt, background=background_color, opts=(opt,) ) )
self.out("".join(line), background=background_color)
# colorizer.demo()
""" Verbose Subprocess """ """ :param popenargs: 'args' for subprocess.Popen() :param env_updates: dict to overwrite os.environ. :param timeout: pass to subprocess.Popen() :param kwargs: pass to subprocess.Popen() """
# subprocess doesn't accept Path() objects
env.update(env_updates)
args=colorizer.colorize(self.args_str, foreground="cyan", opts=("bold",)), kwargs=", ".join(kwargs_txt) )
txt += colorizer.colorize(" env:", foreground="magenta", opts=("bold",)) txt += colorizer.colorize(repr(self.env_updates), opts=("bold",))
colorizer.err(txt, foreground="red", flush=True) else:
""" run subprocess.call()
:param check: if True and subprocess exit_code !=0: sys.exit(exit_code) after run. :return: process exit code """
except KeyboardInterrupt: print("\nExit %r\n" % self.args_str, flush=True) exit_code=None # good idea?!?
sys.exit(exit_code)
""" run subprocess.check_output()
:param check: if True and subprocess exit_code !=0: sys.exit(exit_code) after run. :return: process output """
sys.exit(err.returncode)
""" A subprocess with tee ;) """ self.print_call_info()
orig_timeout = self.kwargs.pop("timeout")
self.kwargs.update({ "stdout":subprocess.PIPE, "stderr":subprocess.STDOUT, })
proc=subprocess.Popen(self.popenargs, **self.kwargs)
end_time = time.time() + orig_timeout for line in iter(proc.stdout.readline, ''): yield line
if time.time()>end_time: raise subprocess.TimeoutExpired(self.popenargs, orig_timeout)
if check and proc.returncode: sys.exit(proc.returncode)
for line in self.iter_output(check=check): print(line, flush=True)
if sys.platform == 'win32': return "pip3.exe" else: return "pip3"
try: return func(*args, **kwargs) except Exception as err: traceback.print_exc(file=sys.stderr) return "%s: %s" % (err.__class__.__name__, err)
""" Enhanced version of 'Cmd' class: - command alias - methods can be called directly from commandline: e.g.: ./foobar.py --help - Display """
"q": "quit", "EOF": "quit", "exit": "quit", "": "help", # Just hit ENTER -> help "--help": "help", "-h": "help", "-?": "help", }
# Will be append to 'doc_leader' in self.do_help():
else:
filename=self.self_filename, version=self.version )
'\n{intro_line}\n' 'Type help or ? to list commands.\n' ).format(intro_line=intro_line)
"\nHint: All commands can be called directly from commandline.\n" "e.g.: $ ./{filename} help\n" ).format( filename=self.self_filename, )
# e.g.: $ bootstrap_env_admin.py boot /tmp/bootstrap_env-env -> run self.do_boot("/tmp/bootstrap_env-env") on startup
""" Called on an input line when the command prefix is not recognized. """
def _complete_list(self, items, text, line, begidx, endidx): if text: return [x for x in items if x.startswith(text)] else: return items
def _complete_path(self, text, line, begidx, endidx): """ complete a command argument with a existing path
usage e.g.: class FooCmd(Cmd2): def complete_foobar(self, text, line, begidx, endidx): return self._complete_path(text, line, begidx, endidx)
def do_foobar(self, path): # 'path' is type string! print("path:", path) """ try: destination = line.split(" ", 1)[1] except IndexError: destination = "."
if destination=="~": return [os.sep]
destination = Path(destination).expanduser().resolve()
if not destination.is_dir(): destination = destination.parent.resolve()
if destination.is_dir(): complete_list = [x.stem + os.sep for x in destination.iterdir() if x.is_dir()] if text: if text in complete_list: return [text + os.sep]
complete_list = [x for x in complete_list if x.startswith(text)] else: complete_list = []
return complete_list
""" return the first line of the DocString. If no DocString: return None """
""" List available commands with "help" or detailed help with "help cmd". """ # Help for one command return super().do_help(arg)
# List available commands:
cmd=command, doc=doc_line ))
"Exit this interactiv shell" print("\n\nbye") return True
""" 1. Apply alias list 2. print first DocString line (if exists), before start the command """
# stop if we are called with commandline arguments
super().__init__(with_pip=True) self.requirements = requirements
print(" * Create new pylucid virtualenv here: %r" % env_dir)
if "VIRTUAL_ENV" in os.environ: print("\nERROR: Don't call me in a activated virtualenv!") print("You are in VIRTUAL_ENV: %r" % os.environ["VIRTUAL_ENV"]) return
return super().create(env_dir)
print(" * Create the directories for the environment.") return super().ensure_directories(env_dir)
print(" * Create 'pyvenv.cfg' configuration file.") return super().create_configuration(context)
print(" * Set up a Python executable in the environment.") return super().setup_python(context)
""" Do the same as bin/activate so that <args> runs in a "activated" virtualenv. """ kwargs.update({ "env_updates": { "VIRTUAL_ENV": context.env_dir, "PATH": "%s:%s" % (context.bin_path, os.environ["PATH"]), } }) VerboseSubprocess(*args, **kwargs).verbose_call( check=check # sys.exit(return_code) if return_code != 0 )
print(" * Install pip in a virtual environment.") # install pip with ensurepip: super()._setup_pip(context)
print(" * Upgrades pip in a virtual environment.") # Upgrade pip first (e.g.: running python 3.5)
context.pip_bin=Path(context.bin_path, get_pip_file_name()) # e.g.: .../bin/pip3 assert context.pip_bin.is_file(), "Pip not found here: %s" % context.pip_bin
if sys.platform == 'win32': # Note: On windows it will crash with a PermissionError: [WinError 32] # because pip can't replace himself while running ;) # Work-a-round is "python -m pip install --upgrade pip" # see also: https://github.com/pypa/pip/issues/3804 self.call_new_python( context, context.env_exe, "-m", "pip", "install", "--upgrade", "pip", check=False # Don't exit on errors ) else: self.call_new_python( context, str(context.pip_bin), "install", "--upgrade", "pip", check=False # Don't exit on errors )
print(" * Set up scripts into the created environment.") return super().setup_scripts(context)
""" Set up any packages which need to be pre-installed into the virtual environment being created.
:param context: The information for the virtual environment creation request being processed. """ print(" * post-setup modification")
# Install bootstrap_env # in normal mode as package from PyPi # in dev. mode as editable from github self.call_new_python( context, str(context.pip_bin), "install", # "--verbose", *self.requirements )
# Check if ".../bin/bootstrap_env_admin.py" exists bootstrap_env_admin_path = Path(context.bin_path, ADMIN_FILE_NAME) if not bootstrap_env_admin_path.is_file(): print("ERROR: admin script not found here: '%s'" % bootstrap_env_admin_path) VerboseSubprocess("ls", "-la", str(context.bin_path)).verbose_call() sys.exit(-1)
# Install all requirements self.call_new_python( context, context.env_exe, str(bootstrap_env_admin_path), "update_env", timeout=240 ) # extended timeout for slow Travis ;)
return Path(path).expanduser().resolve()
# print("text: %r" % text) # print("line: %r" % line) return self._complete_path(text, line, begidx, endidx)
requirements = [] for line in requirement_string.splitlines(): line = line.strip() if line and not line.startswith("#"):
line = line.split("# ", 1)[0] # Remove pip-compile comments e.g.: "... # via foo" line = line.rstrip()
if line.startswith("-e"): # split editables requirements += line.split(" ") else: requirements.append(line) return requirements
""" Create a pylucid virtualenv and install requirements. """
builder = EnvBuilder(requirements) builder.create(str(destination))
self.stdout.write("\n")
if not destination.is_dir(): self.stdout.write("ERROR: Creating virtualenv!\n") sys.exit(1) else: self.stdout.write("virtualenv created at: '%s'\n" % destination)
""" Bootstrap pylucid virtualenv in "normal" mode.
usage: pylucid_boot> boot [path]
Create a pylucid virtualenv in the given [path]. Install packages via PyPi and read-only sources from github.
The destination path must not exist yet!
(used the requirements/normal_installation.txt) """
""" Bootstrap pylucid virtualenv in "developer" mode. All own projects installed as editables via github HTTPS (readonly)
**Should be only used for developing/contributing. All others: Use normal 'boot' ;) **
usage: pylucid_boot> boot_developer [path]
Create a pylucid virtualenv in the given [path]. Install packages via PyPi and read-only sources from github.
The destination path must not exist yet!
(used the requirements/developer_installation.txt) """ self._boot(destination, requirements=DEVELOPER_INSTALL)
if __name__ == '__main__': main() |