def refresh_entry_points(self): """Refresh entry point from saved json and/or build folder""" if runez.DRYRUN: return self._entry_points = self.get_entry_points() if self._entry_points: runez.save_json(self._entry_points, self.entry_points_path, fatal=False)
def check_install_from_pypi(cli, delivery, package, simulate_version=None): cli.run("--debug", "-d%s" % delivery, "install", package) assert cli.succeeded assert cli.match("Installed %s" % package) assert runez.is_executable(package) m = TrackedManifest.from_file(dot_meta("%s/.manifest.json" % package)) assert str(m) assert m.entrypoints[package] assert m.install_info.args == runez.quoted(cli.args) assert m.install_info.timestamp assert m.install_info.vpickley == __version__ assert m.settings.delivery == delivery assert m.settings.python assert m.version r = runez.run(package, "--version") assert r.succeeded cli.expect_success("--debug auto-upgrade %s" % package, "Skipping auto-upgrade, checked recently") cli.expect_success("install %s" % package, "is already installed") cli.expect_success("check", "is installed") cli.expect_success("list", package) cli.expect_success("upgrade", "is already up-to-date") if simulate_version: m.version = simulate_version runez.save_json(m.to_dict(), dot_meta("%s/.manifest.json" % package)) cli.expect_success( "check", "v%s installed, can be upgraded to" % simulate_version)
def test_json(temp_folder, monkeypatch): assert runez.read_json(None) is None assert runez.represented_json(None) == "null\n" assert runez.represented_json([]) == "[]\n" assert runez.represented_json({}) == "{}\n" assert runez.represented_json("foo") == '"foo"\n' assert runez.represented_json({None: 2}, none=True) == '{\n "null": 2\n}\n' assert runez.represented_json({None: 2}, none="None") == '{\n "None": 2\n}\n' assert runez.represented_json({None: None}, none=True) == '{\n "null": null\n}\n' assert runez.represented_json({None: 1, "foo": None}, none="_null") == '{\n "_null": 1,\n "foo": null\n}\n' with pytest.raises(TypeError): # py3 stdlib can't sort with None key... runez.represented_json({None: 2, "foo": "bar"}, none=True) data = {"a": "x", "b": "y"} assert runez.represented_json(data) == '{\n "a": "x",\n "b": "y"\n}\n' assert runez.represented_json(data, indent=None) == '{"a": "x", "b": "y"}' assert runez.save_json(None, None, fatal=False) == 0 assert not runez.DRYRUN with runez.CaptureOutput(dryrun=True) as logged: assert runez.save_json(data, "sample.json") == 1 assert "Would save" in logged.pop() assert not runez.DRYRUN with runez.CaptureOutput() as logged: with pytest.raises(runez.system.AbortException) as exc: runez.read_json(None, fatal=True) assert "Can't read None" in str(exc) assert "Can't read None" in logged.pop() assert runez.read_json("sample.json") is None assert not logged assert runez.read_json("sample.json", default={}, logger=None) == {} assert not logged with monkeypatch.context() as m: m.setattr(runez.serialize, "open", runez.conftest.exception_raiser(), raising=False) assert runez.save_json(data, "sample.json", fatal=False) == -1 assert "Can't save" in logged.pop() assert runez.save_json(data, "sample.json", logger=logging.debug) == 1 assert "Saved " in logged.pop() with monkeypatch.context() as m: m.setattr(io, "open", runez.conftest.exception_raiser()) with pytest.raises(runez.system.AbortException) as exc: runez.read_json("sample.json", fatal=True, logger=None) assert "Can't read sample.json" in str(exc) assert runez.read_json("sample.json") is None assert not logged
def _refresh_frozen(self): output = self._run_pip("freeze", fatal=False) versions = {} if output: for line in output.split("\n"): name, version = system.despecced(line) versions[name] = version if versions: runez.save_json(versions, self.frozen_path) return versions
def save_manifest(self, entry_points): manifest = TrackedManifest( self.manifest_path, self.settings, entry_points, pinned=self.pinned, version=self.version, ) payload = manifest.to_dict() runez.save_json(payload, self.manifest_path) runez.save_json(payload, os.path.join(self.install_path, ".manifest.json")) return manifest
def _refresh_frozen(self): result = self._run_builtin_module("pip", "freeze", "--all", fatal=False) self._frozen = {} if result.output: for line in result.output.split("\n"): name, version = system.despecced(line) self._frozen[name] = version if self._frozen: runez.save_json(self._frozen, self.frozen_path)
def get_latest(self, force=False): """Tracked in DOT_META/.cache/<package>.latest""" path = self.cfg.cache.full_path("%s.latest" % self.dashed) age = self.cfg.version_check_delay(self) if not force and age and runez.file.is_younger(path, age): latest = TrackedVersion.from_file(path) if latest: return latest latest = TrackedVersion.from_pypi(self) if not latest.problem: runez.save_json(latest.to_dict(), path, fatal=None) return latest
def test_json(temp_folder): assert runez.read_json(None, fatal=False) is None assert runez.save_json(None, None, fatal=False) == 0 data = {"a": "b"} assert not runez.DRYRUN with runez.CaptureOutput(dryrun=True) as logged: assert runez.save_json(data, "sample.json") == 1 assert "Would save" in logged.pop() assert not runez.DRYRUN with runez.CaptureOutput() as logged: assert runez.read_json("sample.json", fatal=False) is None assert "No file" in logged.pop() assert runez.read_json("sample.json", default={}, fatal=False) == {} assert not logged with patch("runez.serialize.open", side_effect=Exception): assert runez.save_json(data, "sample.json", fatal=False) == -1 assert "Couldn't save" in logged.pop() assert runez.save_json(data, "sample.json", logger=logging.debug) == 1 assert "Saved " in logged.pop() with patch("io.open", side_effect=Exception): assert runez.read_json("sample.json", fatal=False) is None assert "Couldn't read" in logged.pop() assert runez.read_json("sample.json", logger=logging.debug) == data assert "Read " in logged.pop() assert runez.read_json("sample.json", default=[], fatal=False) == [] assert "Wrong type" in logged.pop() with runez.CaptureOutput() as logged: # Try with an object that isn't directly serializable, but has a to_dict() function obj = SomeRecord() obj.to_dict = lambda *_: data assert runez.save_json(obj, "sample2.json", logger=logging.debug) == 1 assert "Saved " in logged.pop() assert runez.read_json("sample2.json", logger=logging.debug) == data assert "Read " in logged.pop()
def internal_install(self, force=False, verbose=True): """ :param bool force: If True, re-install even if package is already installed :param bool verbose: If True, show more extensive info """ with SoftLock(self.dist_folder, timeout=system.SETTINGS.install_timeout): self.refresh_desired(force=force) self.version = self.desired.version if not self.desired.valid: return runez.abort("Can't install %s: %s", self.name, self.desired.problem) if not force and self.current.equivalent(self.desired): system.inform( self.desired.representation(verbose=verbose, note="is already installed")) self.cleanup() return prev_entry_points = self.entry_points self.effective_install() new_entry_points = self.entry_points removed = set(prev_entry_points).difference(new_entry_points) if removed: old_removed = runez.read_json(self.removed_entry_points_path, default=[], fatal=False) removed = sorted(removed.union(old_removed)) runez.save_json(removed, self.removed_entry_points_path, fatal=False) # Delete wrapper/symlinks of removed entry points immediately for name in removed: runez.delete(system.SETTINGS.base.full_path(name)) self.cleanup() self.current.set_from(self.desired) self.current.save(fatal=False) msg = "Would install" if runez.DRYRUN else "Installed" system.inform("%s %s" % (msg, self.desired.representation(verbose=verbose)))
def refresh(self, *kinds, **kwargs): """ Args: kinds: Kinds to replay (json and/or token) """ existing = kwargs.pop("existing", False) if not kinds: kinds = (TestSamples.K_DESERIALIZED, TestSamples.K_TOKEN) for kind in kinds: if existing: expected = self.expected_content(kind) if expected is runez.UNSET: continue actual = self.deserialized(kind) path = self.expected_path(kind) runez.save_json(actual, path, keep_none=True, logger=logging.info)
def test_install_pypi(cli): cli.expect_failure("--color install six", "it is not a CLI") assert not os.path.exists(dot_meta("six")) cli.expect_failure("install mgit+foo", "not a valid pypi package name") runez.touch( dot_meta("mgit/.foo")) # Should stay because name starts with '.' runez.touch(dot_meta("mgit/mgit-foo")) # Bogus installation runez.touch(dot_meta("mgit/mgit-0.0.1/foo")) # Oldest should be deleted # Simulate the presence of an old entry point manifest_path = dot_meta("mgit/.manifest.json") runez.save_json(dict(entrypoints=["mgit", "old-mgit-entrypoint"]), manifest_path) runez.touch("old-mgit-entrypoint") assert os.path.exists("old-mgit-entrypoint") time.sleep(0.01) # Ensure 0.0.1 is older than 0.0.2 runez.touch( dot_meta("mgit/mgit-0.0.2/foo")) # Youngest should remain for an hour check_install_from_pypi(cli, "symlink", "mgit") assert not os.path.exists("old-mgit-entrypoint") assert os.path.islink("mgit") assert os.path.exists(dot_meta("mgit/.manifest.json")) assert os.path.exists(dot_meta("mgit/.foo")) assert os.path.exists(dot_meta("mgit/mgit-0.0.2")) assert not os.path.exists(dot_meta("mgit/mgit-foo")) assert not os.path.exists(dot_meta("mgit/mgit-0.0.1")) cfg = PickleyConfig() cfg.set_base(".") pspec = PackageSpec(cfg, "mgit") pspec.groom_installation(keep_for=0) assert not os.path.exists(dot_meta("mgit/mgit-0.0.2")) cli.expect_success("uninstall mgit", "Uninstalled mgit") assert not runez.is_executable("mgit") assert not os.path.exists(dot_meta("mgit")) assert os.path.exists(dot_meta("audit.log")) check_install_from_pypi(cli, "wrap", "mgit", simulate_version="0.0.0") check_is_wrapper("mgit", True)
def test_to_dict(temp_folder): with runez.CaptureOutput() as logged: # Try with an object that isn't directly serializable, but has a to_dict() function data = {"a": "b"} obj = SomeRecord() obj.to_dict = lambda *_: data assert runez.save_json(obj, "sample2.json", logger=logging.debug) == 1 assert "Saved " in logged.pop() assert runez.read_json("sample2.json") == data assert not logged
def test_facultative(cli): runez.save_json({"pinned": { "virtualenv": { "facultative": True } }}, dot_meta("config.json")) # Empty file -> proceed with install as if it wasn't there runez.touch("virtualenv") cli.expect_success("-n install virtualenv", "Would state: Installed virtualenv") # Simulate pickley wrapper runez.write("virtualenv", "echo installed by pickley") runez.make_executable("virtualenv") cli.expect_success("-n install virtualenv", "Would state: Installed virtualenv") # Unknown executable -> skip pickley installation (since facultative) runez.write("virtualenv", "echo foo") runez.make_executable("virtualenv") cli.expect_success( "-n install virtualenv", "Skipping installation of virtualenv: not installed by pickley") cli.expect_success("-n check virtualenv", "skipped, not installed by pickley") # --force ignores 'facultative' setting cli.expect_failure("-n install --force virtualenv", "Can't automatically uninstall virtualenv") # Simulate pickley symlink delivery dummy_target = dot_meta("foo") runez.touch(dummy_target) runez.symlink(dummy_target, "virtualenv") cli.expect_success("-n install virtualenv", "Would state: Installed virtualenv")
def save(self): runez.save_json(self.to_dict(), self._path, fatal=False)
def test_install(cli): cli.expect_success("--dryrun --delivery wrap install tox", "Would wrap", "Would install tox") cli.expect_success("--dryrun --delivery symlink install tox", "Would symlink", "Would install tox") assert not os.path.exists(".pickley/audit.log") cli.expect_failure("check tox", "is not installed") cli.expect_failure("install six", "'six' is not a CLI") # Install tox, but add a few files + a bogus previous entry point to test cleanup runez.write(".pickley/tox/.entry-points.json", '["tox-old1", "tox-old2"]\n') runez.touch("tox-old1") runez.touch(".pickley/tox/tox-0.1/bin") runez.touch(".pickley/tox/tox-0.2/bin") runez.touch(".pickley/tox/tox-0.3/bin") runez.touch(".pickley/tox/tox-old1-0.1") runez.touch(".pickley/tox/tox-old1-0.2") cli.expect_success("--delivery wrap install tox", "Installed tox") # Old entry point removed immediately assert not os.path.exists("tox-old1") # Only 1 cleaned up immediately (latest + 1 kept) assert not os.path.exists(".pickley/tox/tox-0.1") assert not os.path.exists(".pickley/tox/tox-0.2") assert os.path.exists(".pickley/tox/tox-0.3") assert not os.path.exists(".pickley/tox/tox-old1-0.1") assert os.path.exists(".pickley/tox/tox-old1-0.2") assert runez.is_executable("tox") result = run_program("tox", "--version") assert "tox" in result.output cli.expect_success("auto-upgrade tox", "Skipping auto-upgrade") runez.delete(system.SETTINGS.meta.full_path("tox", ".ping")) cli.expect_success("auto-upgrade tox", "already installed") cli.expect_success("copy .pickley/tox tox-copy", "Copied") cli.expect_success("move tox-copy tox-relocated", "Moved") runez.delete("tox-relocated") # Verify that older versions and removed entry-points do get cleaned up runez.save_json({"install_timeout": 0}, "custom-timeout.json") cli.expect_success("-ccustom-timeout.json install tox", "already installed") # All cleaned up when enough time went by assert not os.path.exists(".pickley/tox/tox-0.3") assert not os.path.exists(".pickley/tox/tox-old1-0.2") cli.expect_success("check", "tox", "is installed") cli.expect_success( "check --verbose", "tox", "is installed (as %s wrap, channel: " % system.VENV_PACKAGER) # Simulate new version available latest = runez.read_json(".pickley/tox/.latest.json") latest["version"] = "10000.0" runez.save_json(latest, ".pickley/tox/.latest.json") cli.expect_failure("check", "tox", "can be upgraded to 10000.0") # Latest twine 2.0 requires py3 cli.expect_success("-ppex install twine==1.14.0", "Installed twine") cli.expect_success("list", "tox", "twine") cli.expect_success("list --verbose", "tox", "twine") assert find_uninstaller("tox") assert find_uninstaller("twine") cli.expect_success("uninstall twine", "Uninstalled twine") runez.write(".pickley/tox/.current.json", "") cli.expect_failure("check", "tox", "Couldn't read", "is not installed") cli.expect_success("uninstall --all", "Uninstalled tox", "entry points") assert not os.path.exists("tox") assert not os.path.exists(".pickley")
def test_install(cli): tox = system.SETTINGS.base.full_path("tox") p = PACKAGERS.resolved("tox") p.refresh_desired() tox_version = p.desired.version assert not os.path.exists(tox) assert runez.first_line(tox) is None cli.expect_success("--dryrun -b{base} --delivery wrap install tox", "Would wrap", "Would install tox", base=cli.context) cli.expect_success("--dryrun -b{base} --delivery symlink install tox", "Would symlink", "Would install tox", base=cli.context) cli.expect_failure("--dryrun -b{base} --delivery foo install tox", "invalid choice: foo", base=cli.context) cli.expect_success("--dryrun uninstall /dev/null --force", "Nothing to uninstall") runez.touch("foo") assert os.path.exists("foo") cli.expect_failure("uninstall foo", "foo was not installed with pickley") cli.expect_success("uninstall foo --force", "Uninstalled foo") assert not os.path.exists("foo") assert runez.ensure_folder("foo", folder=True) == 1 cli.expect_failure("uninstall foo --force", "Can't automatically uninstall") cli.expect_failure("-b{base} check tox foo/bar", "is not installed", "can't determine latest version", base=cli.context) cli.expect_failure("-b{base} install six", "'six' is not a CLI", base=cli.context) # Install tox, but add a few files + a bogus previous entry point to test cleanup wep1 = system.SETTINGS.base.full_path("tox-old-entrypoint1") tep10 = system.SETTINGS.meta.full_path("tox", "tox-old-entrypoint1-1.0") tep11 = system.SETTINGS.meta.full_path("tox", "tox-old-entrypoint1-1.1") t00 = system.SETTINGS.meta.full_path("tox", "tox-0.0.0") tfoo = system.SETTINGS.meta.full_path("tox", "tox-foo") runez.touch(wep1) runez.touch(tep10) runez.touch(tep11) runez.touch(t00) runez.touch(tfoo) eppath = system.SETTINGS.meta.full_path("tox", ".entry-points.json") runez.write(eppath, '["tox-old-entrypoint1", "tox-old-entrypoint2"]\n') cli.expect_success("-b{base} --delivery wrap install tox", "Installed tox", base=cli.context) # Old entry point removed immediately assert not os.path.exists(wep1) # Only 1 cleaned up immediately (latest + 1 kept) assert not os.path.exists(tep10) assert os.path.exists(tep11) assert not os.path.exists(t00) assert os.path.exists(tfoo) assert runez.is_executable(tox) output = run_program(tox, "--version") assert "tox" in output assert tox_version in output cli.expect_success("-b{base} auto-upgrade tox", "Skipping auto-upgrade", base=cli.context) runez.delete(system.SETTINGS.meta.full_path("tox", ".ping")) cli.expect_success("-b{base} auto-upgrade tox", "already installed", base=cli.context) version = output.partition(" ")[0] cli.expect_success("copy .pickley/tox/tox-%s tox-copy" % version, "Copied") cli.expect_success("move tox-copy tox-relocated", "Moved") # Verify that older versions and removed entry-points do get cleaned up runez.save_json({"install_timeout": 0}, "custom-timeout.json") cli.expect_success("-b{base} -ccustom-timeout.json install tox", "already installed", base=cli.context) # All cleaned up when enough time went by assert not os.path.exists(tep10) assert not os.path.exists(tep11) assert not os.path.exists(t00) assert not os.path.exists(tfoo) cli.expect_success("-b{base} check", "tox", "is installed", base=cli.context) cli.expect_success( "-b{base} check --verbose", "tox", "is installed (as %s wrap, channel: " % system.VENV_PACKAGER, base=cli.context, ) p = PACKAGERS.get(system.VENV_PACKAGER)("tox") p.refresh_latest() p.latest.version = "10000.0" p.latest.save() cli.expect_failure("-b{base} check", "tox", "can be upgraded to 10000.0", base=cli.context) cli.expect_success("-b{base} -ppex install twine", "Installed twine", base=cli.context) cli.expect_success("-b{base} list", "tox", "twine", base=cli.context) cli.expect_success("-b{base} list --verbose", "tox", "twine", base=cli.context) tmp = os.path.realpath(cli.context) assert find_uninstaller(os.path.join(tmp, "tox")) assert find_uninstaller(os.path.join(tmp, "twine")) cli.expect_success("-b{base} uninstall twine", "Uninstalled twine", base=cli.context) runez.delete(p.current._path) runez.touch(p.current._path) cli.expect_failure("-b{base} check", "tox", "Couldn't read", "is not installed", base=cli.context) cli.expect_success("-b{base} uninstall tox", "Uninstalled tox", "entry points", base=cli.context)