def package(self): """Package pypi module with 'self.name'""" if not self.version and not self.source_folder: return runez.abort( "Need either source_folder or version in order to package", fatal=(True, [])) if not self.version: setup_py = os.path.join(self.source_folder, "setup.py") if not os.path.isfile(setup_py): return runez.abort("No setup.py in %s", short(self.source_folder), fatal=(True, [])) self.version = system.run_python(setup_py, "--version", dryrun=False, fatal=False, package_name=self.name) if not self.version: return runez.abort("Could not determine version from %s", short(setup_py), fatal=(True, [])) self.pip_wheel() self.refresh_entry_points() runez.ensure_folder(self.dist_folder, folder=True) template = "{name}" if self.source_folder else "{name}-{version}" self.packaged = [] self.effective_package(template)
def represented(self): """ :return str: Human readable representation of these settings """ if not self.contents: return " - %s: # empty" % short(self.path) result = [" - %s:" % short(self.path)] add_representation(result, self.contents, indent=" ") return "\n".join(result)
def get_definition(self, key): """ :param str key: Key to look up :return Definition|None: Definition corresponding to 'key' in this settings file, if any """ if not key: return None if "." in key: prefix, _, leaf = key.rpartition(".") definition = self.get_definition(prefix) if not definition: return None if isinstance(definition.value, dict): value = definition.value.get(leaf) if value is not None: return Definition(value, self, key) return None if definition.value is not None: LOG.debug("'%s' is of type %s (not a dict) in '%s'", prefix, type(definition.value), short(self.path)) return None value = self.contents.get(key) if value is not None: return Definition(value, self, key) return None
def test_custom_settings(): s = pickley.settings.Settings(sample_path("custom")) s.load_config() assert str(s) == "[4] base: %s" % short(s.base.path) assert str(s.defaults) == "defaults" assert str(s.base) == "base: %s" % short(s.base.path) assert s.get_definition("") is None assert s.resolved_definition("") is None assert s.resolved_value("foo") is None p = s.base.full_path("foo/bar") assert s.base.relative_path(p) == "foo/bar" d = s.resolved_definition("delivery", package_name="dict_sample") assert str(d) == "config.json:delivery.copy" assert s.resolved_value("delivery", package_name="tox") == "venv" assert s.resolved_value("delivery", package_name="virtualenv") == "wrap" assert s.resolved_value("packager", package_name="tox") == system.VENV_PACKAGER assert s.resolved_value("packager", package_name="virtualenv") == "pex" assert s.resolved_packages("bundle:dev") == ["tox", "twine"] assert s.get_value("bundle.dev") == ["tox", "twine"] assert s.get_value("bundle.dev2") == ["tox", "twine", "pipenv"] old_width = pickley.settings.REPRESENTATION_WIDTH pickley.settings.REPRESENTATION_WIDTH = 40 actual = s.represented(include_defaults=False).replace( short(s.base.path), "{base}") assert actual == EXPECTED_REPRESENTATION.strip() pickley.settings.REPRESENTATION_WIDTH = old_width s.cli.contents["packager"] = "copy" d = s.resolved_definition("packager") assert d.value == "copy" assert d.source is s.cli d = s.get_definition("packager") assert d.value == "copy" assert d.source is s.cli assert s.install_timeout == 2 assert s.version_check_seconds == 60
def sanity_check(self, args): """ :param str args: Args to run as sanity-check against all packaged exes, example: "--version" """ if args: for path in self.executables: output = runez.run(path, args) print("Sanity check: %s %s -> %s" % (short(path), args, output))
def uninstall_existing(target, fatal=True): """ :param str target: Path to executable to auto-uninstall if needed :param bool|None fatal: Abort execution on failure if True :return int: 1 if successfully uninstalled, 0 if nothing to do, -1 if failed """ handler = find_uninstaller(target) if handler: return handler(target, fatal=fatal) return runez.abort("Can't automatically uninstall %s", short(target), fatal=(fatal, -1))
def install(self, force=False): """ :param bool force: If True, re-install even if package is already installed """ try: self.internal_install(force=force) except SoftLockException as e: LOG.error("%s is currently being installed by another process" % self.name) runez.abort("If that is incorrect, please delete %s.lock", short(e.folder))
def add_representation(result, data, indent=""): """ :param list result: Where to add lines representing 'data' :param dict|list|str data: Data to represent :param str indent: Indentation to use """ if not data: return if isinstance(data, list): for item in data: result.append("%s- %s" % (indent, short(item))) return if isinstance(data, dict): for key, value in sorted(data.items()): if isinstance(value, list): brief = runez.represented_args(value, separator=", ") if len(brief) < REPRESENTATION_WIDTH: result.append("%s%s: [%s]" % (indent, short(key), brief)) continue if isinstance(value, (dict, list)): result.append("%s%s:" % (indent, short(key))) add_representation(result, value, indent=" %s" % indent) else: result.append("%s%s: %s" % (indent, short(key), short(value))) return result.append("%s- %s" % (indent, short(data)))
def test_package(cli): pickley = system.SETTINGS.base.full_path("dist", "pickley", "bin", "pickley") expected_version = system.run_python(os.path.join(PROJECT, "setup.py"), "--version") # Package pickley as venv cli.expect_success(["package", "-d", "dist", PROJECT], "Packaged %s successfully" % short(PROJECT)) # Verify that it packaged OK, and is relocatable assert runez.is_executable(pickley) assert run_program(pickley, "--version") == expected_version assert runez.first_line(pickley).startswith("#!/usr/bin/env python")
def main(ctx, debug, dryrun, base, index, config, python, delivery, packager): """ Package manager for python CLIs """ if any(opt in sys.argv for opt in ctx.help_option_names): # pragma: no cover return if dryrun: debug = True if base: base = runez.resolved_path(base) if not os.path.exists(base): sys.exit("Can't use %s as base: folder does not exist" % short(base)) system.SETTINGS.set_base(base) if not dryrun and ctx.invoked_subcommand in AUDITED: file_location = system.SETTINGS.meta.full_path("audit.log") else: file_location = None runez.log.setup( debug=debug, dryrun=dryrun, greetings=":: {argv}", console_format="%(levelname)s %(message)s" if debug else "%(message)s", console_level=logging.WARNING, console_stream=sys.stderr, file_format= "%(asctime)s %(timezone)s [%(process)d] %(context)s%(levelname)s - %(message)s", file_level=logging.DEBUG, file_location=file_location, locations=None, rotate="size:500k", rotate_count=1, ) runez.log.silence("pip") # Disable logging.config, as pip tries to get smart and configure all logging... logging.config.dictConfig = lambda x: None system.SETTINGS.load_config(config=config, delivery=delivery, index=index, packager=packager) system.DESIRED_PYTHON = python
def package(build, dist, symlink, relocatable, sanity_check, folder): """ Package a project from source checkout """ build = runez.resolved_path(build) dist = runez.resolved_path(dist) folder = runez.resolved_path(folder) system.SETTINGS.meta = meta_folder(build) if not os.path.isdir(folder): sys.exit("Folder %s does not exist" % short(folder)) setup_py = os.path.join(folder, "setup.py") if not os.path.exists(setup_py): sys.exit("No setup.py in %s" % short(folder)) with runez.CurrentFolder(folder): # Some setup.py's assume their working folder is the folder where they're in name = system.run_python(setup_py, "--name", fatal=False, dryrun=False) if not name: sys.exit("Could not determine package name from %s" % short(setup_py)) runez.Anchored.add(folder) p = PACKAGERS.resolved(name) p.build_folder = build p.dist_folder = dist p.relocatable = relocatable p.source_folder = folder p.package() p.create_symlinks(symlink) p.sanity_check(sanity_check) print("Packaged %s successfully, produced: %s" % (short(folder), runez.represented_args(p.executables))) runez.Anchored.pop(folder)
def test_bogus_install(cli): cli.expect_failure("-b foo/bar settings", "Can't use foo/bar as base: folder does not exist") cli.expect_failure("auto-upgrade foo", "not currently installed") cli.expect_failure("package foo/bar", "Folder", "does not exist") cli.expect_failure(["package", cli.context], "No setup.py") runez.touch(os.path.join(cli.context, "setup.py")) cli.expect_failure(["package", cli.context], "Could not determine package name") cli.expect_success("-b{base} check", "No packages installed", base=cli.context) cli.expect_success("-b{base} list", "No packages installed", base=cli.context) cli.expect_success("settings -d", "base: %s" % short(cli.context))
def represented(self, include_defaults=True): """ :param bool include_defaults: When True, include representation of defaults as well :return str: Human readable representation of these settings """ result = [ "settings:", " base: %s" % short(self.base.path), ] if self.index: result.append(" index: %s" % self.index) result.append("") result.append(" config:") result.append(self.cli.represented()) for child in self.children: result.append(child.represented()) if include_defaults: result.append(self.defaults.represented()) return "\n".join(result).strip()
def install(self, target, source): """ :param str target: Full path of executable to deliver (<base>/<entry_point>) :param str source: Path to original executable being delivered (.pickley/<package>/...) """ runez.delete(target, logger=None) if runez.DRYRUN: LOG.debug("Would %s %s (source: %s)", self.implementation_name, short(target), short(source)) return if not os.path.exists(source): runez.abort("Can't %s, source %s does not exist", self.implementation_name, short(source)) try: LOG.debug("Delivering %s %s -> %s", self.implementation_name, short(target), short(source)) self._install(target, source) except Exception as e: runez.abort("Failed %s %s: %s", self.implementation_name, short(target), e)
def settings(diagnostics): """ Show settings """ if diagnostics: prefix = getattr(sys, "prefix", None) real_prefix = getattr(sys, "real_prefix", None) print("python : %s" % short(system.target_python(desired=system.INVOKER, fatal=None), meta=False)) print("sys.executable : %s" % short(sys.executable, meta=False)) print("sys.prefix : %s" % short(prefix, meta=False)) if real_prefix: print("sys.real_prefix: %s" % short(real_prefix, meta=False)) if not system.SETTINGS.meta.path.startswith( system.PICKLEY_PROGRAM_PATH): print("pickley : %s" % short(system.PICKLEY_PROGRAM_PATH, meta=False)) print("meta : %s" % short(system.SETTINGS.meta.path, meta=False)) print("") print(system.SETTINGS.represented())
def test_settings(cli): cli.expect_success("settings -d", "settings:", "base: %s" % short(system.SETTINGS.base.path))
def __repr__(self): return short(self.path)
def __repr__(self): return "%s:%s" % (short(self.source), self.key)
def move(source, destination): """ Copy file or folder, relocate venvs accordingly (if any) """ system.move(source, destination) print("Moved %s -> %s" % (short(source), short(destination)))