def _generate(self): putup([self.name]) with chdir(self.name): demoapp_src_dir = __location__ / self.name demoapp_dst_root = self.pkg_path demoapp_dst_pkg = demoapp_dst_root / "src" / self.name copyfile(demoapp_src_dir / "runner.py", demoapp_dst_pkg / "runner.py") git("add", demoapp_dst_pkg / "runner.py") for file in "setup.cfg setup.py pyproject.toml".split(): copyfile(demoapp_src_dir / file, demoapp_dst_root / file) git("add", demoapp_dst_root / file) if self.data: data_src_dir = demoapp_src_dir / "data" data_dst_dir = demoapp_dst_pkg / "data" os.mkdir(data_dst_dir) pkg_file = data_dst_dir / "__init__.py" pkg_file.write_text("") git("add", pkg_file) for file in "hello_world.txt".split(): copyfile(data_src_dir / file, data_dst_dir / file) git("add", data_dst_dir / file) git("commit", "-m", "Added basic application logic") # this is needed for Windows 10 which lacks some certificates self.run("pip", "install", "-q", "certifi")
def test_putup_with_update_dirty_workspace(cwd, putup): run(f"{putup} myproj") with chdir("myproj"): with open("setup.py", "w") as fh: fh.write("DIRTY") with pytest.raises(CalledProcessError): run(f"{putup} --update myproj") run(f"{putup} --update myproj --force")
def build(self, dist="bdist", cli_opts=()): with self.guard("built"), chdir(self.pkg_path): if "wheel" in dist: self.run("pip", "install", "wheel") else: cli_opts = cli_opts or ["--format", "gztar"] # ^ force tar.gz (Windows defaults to zip) self.run("python", "setup.py", dist, *cli_opts) self.dist = dist return self
def run(self, cmd, with_coverage=False, **kwargs): if with_coverage: kwargs.setdefault("pytestconfig", self.pytestconfig) # change to directory where .coverage needs to be created kwargs.setdefault("cd", os.getcwd()) return self.venv.run_with_coverage(cmd, **kwargs).strip() else: with chdir(self.tmpdir): kwargs.setdefault("cwd", self.tmpdir) return self.venv.run(cmd, capture=True, **kwargs).strip()
def _install_bdist(self): setupcfg = info.read_setupcfg(self.pkg_path) requirements = deps.split( setupcfg["options"]["install_requires"].value) self.run("pip", "install", *requirements) with chdir("/"): # Because of the way bdist works, the tar.gz will contain # the whole path to the current venv, starting from the # / directory ... untar(self.dist_file, "--force-local")
def test_get_git_root_with_nonegit(tmpfolder, nonegit_mock): project = "my_project" struct = { "my_file": "Some other content", "my_dir": {"my_file": "Some more content"}, } structure.create_structure(struct, {"project_path": project}) with chdir(project): git_root = repo.get_git_root(default=".") assert git_root == "."
def install(self, edit=False): with self.guard("installed"), chdir(self.pkg_path): self.check_not_installed() if edit or self.dist is None: self.run("pip", "install", "-e", ".") elif self.dist == "bdist": self._install_bdist() else: self.run("pip", "install", self.dist_file) return self
def test_generated_extension(tmpfolder): args = ["--no-config", "--venv", "--pre-commit", EXT_FLAG, "my_project"] # --no-config: avoid extra config from dev's machine interference # --venv: generate a venv so we can install the resulting project # --pre-commit: ensure generated files respect repository conventions cli.main(args) with chdir("my_project"): # Testing a project generated by the custom extension run_common_tasks()
def test_add_custom_extension(tmpfolder): args = ["--no-config", "-vv", EXT_FLAG, "my_project", "-p", "my_package"] # --no-config: avoid extra config from dev's machine interference cli.main(args) with chdir("my_project"): assert not Path("README.rst").exists() for path in EXPECTED_DIRS: assert Path(path).is_dir() for path in EXPECTED_FILES: assert Path(path).is_file()
def test_get_git_root(tmpfolder): project = "my_project" struct = { "my_file": "Some other content", "my_dir": {"my_file": "Some more content"}, } structure.create_structure(struct, {"project_path": project}) repo.init_commit_repo(project, struct) with chdir(project): git_root = repo.get_git_root() assert Path(git_root).name == project
def test_pretend_chdir(caplog, tmpdir): caplog.set_level(logging.INFO) curr_dir = os.getcwd() dname = uniqstr() # Use a unique name to get easily identifiable logs temp_dir = str(tmpdir.mkdir(dname)) with fs.chdir(temp_dir, pretend=True): new_dir = os.getcwd() assert new_dir == curr_dir # the directory is not changed assert curr_dir == os.getcwd() logs = caplog.text assert re.search("chdir.+" + dname, logs)
def test_chdir(caplog, tmpdir, isolated_logger): caplog.set_level(logging.INFO) curr_dir = os.getcwd() dname = uniqstr() # Use a unique name to get easily identifiable logs temp_dir = str(tmpdir.mkdir(dname)) with fs.chdir(temp_dir, log=True): new_dir = os.getcwd() assert new_dir == os.path.realpath(temp_dir) assert curr_dir == os.getcwd() assert curr_dir != new_dir logs = caplog.text assert re.search("chdir.+" + dname, logs)
def test_creators(tmp_path_factory, creator, pretend): folder = tmp_path_factory.mktemp(f"test_creators_{uniqstr()}_") with fs.chdir(folder): # ensure parametrized tests do not share folders path = Path(".venv") creator(path, pretend=pretend) if pretend: assert not path.exists() else: assert path.is_dir() assert list(path.glob("*/python*")) assert list(path.glob("*/pip*")) rmpath(folder)
def test_version_of_subdir(tmpfolder): projects = ["main_project", "inner_project"] for project in projects: opts = cli.parse_args([project]) opts = api.bootstrap_options(opts) _, opts = actions.get_default_options({}, opts) struct, _ = structure.define_structure({}, opts) struct, _ = structure.create_structure(struct, opts) repo.init_commit_repo(project, struct) rm_rf(Path("inner_project", ".git")) move("inner_project", target="main_project/inner_project") # setuptools_scm required explicitly setting the git root when setup.py is # not at the root of the repository nested_setup_py = Path(tmpfolder, "main_project/inner_project/setup.py") content = nested_setup_py.read_text() content = content.replace( "use_scm_version={", 'use_scm_version={"root": "..", "relative_to": __file__, ' ) nested_setup_py.write_text(content) nested_pyproject_toml = Path(tmpfolder, "main_project/inner_project/pyproject.toml") config = toml.loads(nested_pyproject_toml.read_text()) config["tool"]["setuptools_scm"]["root"] = ".." nested_pyproject_toml.write_text(toml.dumps(config)) with chdir("main_project"): main_version = ( subprocess.check_output([sys.executable, "setup.py", "--version"]) .strip() .splitlines()[-1] ) with chdir("inner_project"): inner_version = ( subprocess.check_output([sys.executable, "setup.py", "--version"]) .strip() .splitlines()[-1] ) assert main_version.strip() == inner_version.strip()
def __init__(self, tmpdir, venv, data=None): self.name = "demoapp" if data: self.name += "_data" self.pkg_path = Path(str(tmpdir), self.name) self.built = False self.installed = False self.venv = venv self.venv_path = Path(str(venv.virtualenv)) self.venv_bin = Path(str(venv.python)) self.data = data self.dist = None with chdir(str(tmpdir)): self._generate()
def test_inplace_update(with_coverage, venv_mgr): # Given an existing project project = Path(venv_mgr.tmpdir) / "my-ns-proj" (venv_mgr.install_this_pyscaffold().putup( f"--package project --namespace my_ns {project}")) # With an existing configuration parser = ConfigParser() parser.read(project / "setup.cfg") assert parser["metadata"]["name"] == "my-ns-proj" assert parser["pyscaffold"]["package"] == "project" assert parser["pyscaffold"]["namespace"] == "my_ns" # And without some extensions for file in (".pre-commit-config.yaml", ".isort.cfg"): assert not Path(project, file).exists() # When the project is updated # without repeating the information already given # but adding some information/extensions with chdir(str(project)): (venv_mgr.putup( "-vv --description asdf --pre-commit --update .", with_coverage=with_coverage, cwd=str(project), )) # Then existing configuration should be preserved + the additions parser = ConfigParser() parser.read(project / "setup.cfg") assert parser["metadata"]["name"] == "my-ns-proj" assert parser["pyscaffold"]["package"] == "project" assert parser["pyscaffold"]["namespace"] == "my_ns" # Some information (metadata) require manual update # unless the --force option is used assert parser["metadata"]["description"] != "asdf" # New extensions should take effect for file in ("tox.ini", ".pre-commit-config.yaml", ".isort.cfg"): assert Path(project, file).exists() # While using the existing information parser = ConfigParser() parser.read(project / ".isort.cfg") assert parser["settings"]["known_first_party"] == "my_ns"
def with_existing_proj_config(tmp_path): proj = tmp_path / "proj" proj.mkdir(parents=True, exist_ok=True) (proj / "setup.cfg").write_text( dedent( """\ [metadata] name = SuperProj description = some text author = John Doe author-email = [email protected] url = www.example.com license = gpl3 [pyscaffold] package = super_proj """ ) ) with chdir(str(proj)): yield proj
def test_generated_extension(tmpfolder): args = [ "--no-config", # <- avoid extra config from dev's machine interference "--venv", # <- generate a venv that we will use to install the project "--custom-extension", "pyscaffoldext-some_extension", ] cli.main(args) with chdir("pyscaffoldext-some_extension"): try: run_common_tasks() except CalledProcessError as ex: if os.name == "nt" and "too long" in ex.output: pytest.skip("Windows really have a problem with long paths....") else: raise putup = shell.get_executable("putup", prefix=".venv", include_path=False) assert putup run(putup, "--some-extension", "the_actual_project") assert Path("the_actual_project/setup.cfg").exists()
def test_creators(tmp_path_factory, creator, pretend): folder = tmp_path_factory.mktemp(f"test_creators_{uniqstr()}_") with fs.chdir(folder): # ensure parametrized tests do not share folders path = Path(".venv") try: creator(path, pretend=pretend) except Exception: if environ.get("USING_CONDA") == "true": pytest.skip( "Creating venvs more than one level deep inside conda is tricky" "and error prone. Here we use at least 2 levels (tox > venv)." ) else: raise if pretend: assert not path.exists() else: assert path.is_dir() assert list(path.glob("*/python*")) assert list(path.glob("*/pip*")) rmpath(folder)
def setup_py(self, *args, **kwargs): with chdir(self.pkg_path): args = ["python", "-Wignore", "setup.py"] + list(args) # Avoid warnings since we are going to compare outputs return self.run(*args, **kwargs)
def tag(self, name, message): with chdir(self.pkg_path): git("tag", "-a", name, "-m", message) return self
def make_commit(self): with chdir(self.pkg_path): git("commit", "-a", "-m", "message") return self
def get_file(self, path): with chdir(self.tmpdir): return Path(path).read_text()
def setup_py(self, *args, **kwargs): with chdir(self.pkg_path): args = ["python", "setup.py"] + list(args) return self.run(*args, **kwargs)