def test_create_project_with_tox(tmpfolder): # when the project is created, opts = dict(project_path="proj") create_project(opts) # then tox files are created by default assert Path("proj/tox.ini").exists()
def test_pipenv_works_with_pyscaffold(cwd, venv_path, venv_run): # Given a project is create with pyscaffold # and it have some dependencies in setup.cfg create_project(project='myproj', requirements=['appdirs']) with cwd.join('myproj').as_cwd(): # When we install pipenv, venv_run('pip install -v pipenv') # use it to proxy setup.cfg venv_run('pipenv install -e .') # and install things to the dev env, venv_run('pipenv install --dev flake8') # Then it should be able to generate a Pipfile.lock venv_run('pipenv lock') assert exists('Pipfile.lock') # with the correct dependencies with open('Pipfile.lock') as fp: content = json.load(fp) assert content['default']['appdirs'] assert content['develop']['flake8'] # and run things from inside pipenv's venv assert venv_path in venv_run('pipenv run which flake8') venv_run('pipenv run flake8 src/myproj/skeleton.py')
def test_create_project_without_tox(tmpfolder): # when the project is created with no_tox opts = dict(project_path="proj", extensions=[NoTox()]) create_project(opts) # then tox files should not exist assert not Path("proj/tox.ini").exists()
def test_pretend_move_old_package(tmpfolder, caplog, isolated_logger): # Given a package is already created without namespace create_project(project="proj", package="my_pkg") opts = parse_args( ["proj", "-p", "my_pkg", "--namespace", "my.ns", "--pretend"]) opts = process_opts(opts) logger.reconfigure(opts) struct = dict(proj={"src": {"my_pkg": {"file.py": ""}}}) # when 'pretend' option is passed, struct, opts = get_default_options(struct, opts) struct, opts = enforce_namespace_options(struct, opts) struct, opts = move_old_package(struct, opts) # then nothing should happen, assert tmpfolder.join("proj/src/my_pkg/__init__.py").check() assert not tmpfolder.join("proj/src/my/ns").check() # something should be logged, log = caplog.text expected_log = ("move", "my_pkg", "to", str(Path("my/ns"))) for text in expected_log: assert text in log # but user should see no warning, unexpected_warnings = ( "A folder", "exists in the project directory", "a namespace option was passed", "Please make sure", ) for text in unexpected_warnings: assert text not in log
def test_pretend_move_old_package(tmpfolder, caplog, isolated_logger): # Given a package is already created without namespace create_project(project="proj", package="my_pkg") opts = parse_args( ["proj", "-p", "my_pkg", "--namespace", "my.ns", "--pretend"]) opts = process_opts(opts) configure_logger(opts) struct = dict(proj={'src': {'my_pkg': {'file.py': ''}}}) # when 'pretend' option is passed, struct, opts = get_default_options(struct, opts) struct, opts = enforce_namespace_options(struct, opts) struct, opts = move_old_package(struct, opts) # then nothing should happen, assert tmpfolder.join("proj/src/my_pkg/__init__.py").check() assert not tmpfolder.join("proj/src/my/ns").check() # something should be logged, log = caplog.text expected_log = ('move', 'my_pkg', 'to', 'my/ns') for text in expected_log: assert text in log # but user should see no warning, unexpected_warnings = ('A folder', 'exists in the project directory', 'a namespace option was passed', 'Please make sure') for text in unexpected_warnings: assert text not in log
def test_updating_existing_project(tmpfolder, caplog): # Given a project already exists, but was generated without # namespace, create_project(project="my-proj") assert tmpfolder.join("my-proj/src/my_proj").check() assert not tmpfolder.join("my-proj/src/my/ns").check() # when the project is updated with a namespace, create_project( project="my-proj", update=True, namespace="my.ns", extensions=[namespace.Namespace("namespace")], ) # then the package folder should be moved to a nested position, assert not tmpfolder.join("my-proj/src/my_proj").check() assert tmpfolder.join("my-proj/src/my/ns/my_proj").check() # and the user should see a warn expected_warnings = ( "A folder", "exists in the project directory", "a namespace option was passed", "Please make sure", ) log = caplog.text for text in expected_warnings: assert text in log
def test_create_project_when_folder_exists(tmpfolder, git_mock): tmpfolder.ensure("my-project", dir=True) opts = dict(project="my-project") with pytest.raises(DirectoryAlreadyExists): create_project(opts) opts = dict(project="my-project", force=True) create_project(opts)
def test_create_project_generate_extension_files(tmpfolder, git_mock): # Given a blank state, assert not path_exists("proj/tests/extra.file") assert not path_exists("proj/tests/another.file") # and an extension with extra files, def add_files(struct, opts): struct = helpers.ensure(struct, "proj/tests/extra.file", "content") struct = helpers.merge( struct, {"proj": { "tests": { "another.file": "content" } }}) return struct, opts # when the created project is called, create_project(project="proj", extensions=[create_extension(add_files)]) # then the files should be created assert path_exists("proj/tests/extra.file") assert tmpfolder.join("proj/tests/extra.file").read() == "content" assert path_exists("proj/tests/another.file") assert tmpfolder.join("proj/tests/another.file").read() == "content"
def test_pre_commit(tmpfolder): args = ["--custom-extension", "pyscaffoldext-some_extension"] opts = parse_args(args) create_project(opts) assert path_exists("pyscaffoldext-some_extension/.pre-commit-config.yaml")
def test_pipenv_works_with_pyscaffold(tmpfolder, venv_path, venv_run): # Given a project is created with pyscaffold # and it has some dependencies in setup.cfg create_project(project_path="myproj", requirements=["appdirs"]) if any(ch in pyscaffold_version for ch in ("b", "a", "pre", "rc")): flags = "--pre" else: flags = "" with tmpfolder.join("myproj").as_cwd(): # When we install pipenv, venv_run("pip install -v pipenv") venv_run("pipenv --bare install certifi") # use it to proxy setup.cfg venv_run("pipenv --bare install {} -e .".format(flags)) # and install things to the dev env, venv_run("pipenv --bare install --dev flake8") # Then it should be able to generate a Pipfile.lock venv_run("pipenv lock") assert Path("Pipfile.lock").exists() # with the correct dependencies with open("Pipfile.lock") as fp: content = json.load(fp) assert content["default"]["appdirs"] assert content["develop"]["flake8"] # and run things from inside pipenv's venv assert venv_path in venv_run("pipenv run which flake8") venv_run("pipenv --bare run flake8 src/myproj/skeleton.py")
def test_create_project_with_markdown(tmpfolder): # Given options with the markdown extension, opts = dict( project_path="proj", package="pkg", version=pyscaffold_version, extensions=[Markdown()], config_files=api.NO_CONFIG, ) # NO_CONFIG: avoid extra config from dev's machine interference # when the project is created, api.create_project(opts) assert (tmpfolder / "proj/docs").is_dir() # then markdown files should exist, for file in CONV_FILES: assert (tmpfolder / f"proj/{file}.md").exists() assert not (tmpfolder / f"proj/{file}.rst").exists() # the content-type of README should be changed accordingly, existing_setup = (tmpfolder / "proj/setup.cfg").read_text() cfg = ConfigParser() cfg.read_string(existing_setup) assert "text/markdown" in str( cfg["metadata"]["long_description_content_type"]) assert "file: README" in str(cfg["metadata"]["long_description"]) # and new doc requirements should be added to docs/requirements.txt requirements_txt = (tmpfolder / "proj/docs/requirements.txt").read_text() for req in DOC_REQUIREMENTS: assert req in requirements_txt
def build(): create_project( project="my_test_folder", author="Your Name", license="mit", extensions=[ScaffoldDotfiles("my_flask")], )
def test_add_custom_extension(tmpfolder): args = [EXT_FLAG, "my_project", "-p", "my_package"] opts = parse_args(args) opts = process_opts(opts) create_project(opts) assert path_exists("my_project/src/my_package/__init__.py")
def test_no_skeleton(tmpfolder): args = ["--custom-extension", "pyscaffoldext-some_extension"] opts = parse_args(args) create_project(opts) assert not path_exists("pyscaffoldext-some_extension/src/pyscaffoldext/" "some_extension/skeleton.py")
def test_create_project_without_cirrus(tmpfolder): # Given options without the cirrus extension, opts = dict(project_path="proj") # when the project is created, create_project(opts) # then cirrus files should not exist assert not path_exists("proj/.cirrus.yml")
def test_create_project_no_django(tmpfolder, nodjango_admin_mock): # Given options with the django extension, # but without django-admin being installed, opts = dict(project=PROJ_NAME, extensions=[django.Django('django')]) # when the project is created, # then an exception should be raised. with pytest.raises(django.DjangoAdminNotInstalled): create_project(opts)
def test_create_project_without_pre_commit(tmpfolder): # Given options without the pre-commit extension, opts = dict(project="proj") # when the project is created, create_project(opts) # then pre-commit files should not exist assert not path_exists("proj/.pre-commit-config.yaml")
def test_create_project_with_cirrus(tmpfolder): # Given options with the cirrus extension, opts = dict(project_path="proj", extensions=[Cirrus("cirrus")]) # when the project is created, create_project(opts) # then files from cirrus extension should exist assert path_exists("proj/.cirrus.yml")
def test_create_project_without_namespace(tmpfolder): # Given options without the namespace extension, opts = dict(project="proj") # when the project is created, create_project(opts) # then plain structure should exist assert path_exists("proj/src/proj/__init__.py")
def test_create_project_without_tox(tmpfolder): # Given options without the tox extension, opts = dict(project="proj") # when the project is created, create_project(opts) # then tox files should not exist assert not path_exists("proj/tox.ini")
def test_create_project_with_cookiecutter_but_no_template(tmpfolder): # Given options with the cookiecutter extension, but no template opts = dict(project=PROJ_NAME, extensions=[cookiecutter.Cookiecutter('cookiecutter')]) # when the project is created, # then an exception should be raised. with pytest.raises(cookiecutter.MissingTemplate): create_project(opts)
def test_create_project_with_tox(tmpfolder): # Given options with the tox extension, opts = dict(project="proj", extensions=[tox.Tox('tox')]) # when the project is created, create_project(opts) # then tox files should exist assert path_exists("proj/tox.ini")
def test_add_custom_extension(tmpfolder): opts = { "package": "my_extension", "project": "pyscaffoldext-my_project", "extensions": [CustomExtension("custom_ext")] } create_project(opts) assert path_exists("pyscaffoldext-my_project/src/pyscaffoldext/" "my_extension/extension.py")
def test_create_project_without_pyproject(tmpfolder): # Given options without the pyproject extension, opts = dict(project="proj") # when the project is created, create_project(opts) # then pyproject files should not exist assert not path_exists("proj/pyproject.toml")
def test_create_project_without_django(tmpfolder): # Given options without the django extension, opts = dict(project=PROJ_NAME) # when the project is created, create_project(opts) # then django files should not exist for path in DJANGO_FILES: assert not path_exists(path)
def test_create_project_without_cookiecutter(tmpfolder): # Given options without the cookiecutter extension, opts = dict(project=PROJ_NAME) # when the project is created, create_project(opts) # then cookiecutter files should not exist for path in COOKIECUTTER_FILES: assert not path_exists(path)
def test_create_project_without_no_skeleton(tmpfolder): # Given options without the tox extension, opts = dict(project="proj") # when the project is created, create_project(opts) # then skeleton file should exist assert path_exists("proj/src/proj/skeleton.py") assert path_exists("proj/tests/test_skeleton.py")
def test_add_custom_extension_with_namespace(tmpfolder): # we expect the second namespace to be just ignored args = [ "--namespace", "test", "--custom-extension", "pyscaffoldext-some_extension" ] opts = parse_args(args) with pytest.raises(NamespaceError): create_project(opts)
def new_python_project(name: str, author: str): if name is None: name = click.prompt(click.style("Enter your python project name ", fg='blue'), default="My PyProject") if author is None: author = click.prompt(click.style("Enter project authors ", fg='blue')) create_project(project=name, author=author, license="mit")
def test_create_project_with_pre_commit(tmpfolder): # Given options with the pre-commit extension, opts = dict(project="proj", extensions=[pre_commit.PreCommit('pre-commit')]) # when the project is created, create_project(opts) # then pre-commit files should exist assert path_exists("proj/.pre-commit-config.yaml")
def test_create_project_without_travis(tmpfolder): # Given options without the travis extension, opts = dict(project_path="proj") # when the project is created, create_project(opts) # then travis files should not exist assert not Path("proj/.travis.yml").exists() assert not Path("proj/tests/travis_install.sh").exists()
def test_create_project_wit_no_skeleton(tmpfolder): # Given options with the tox extension, opts = dict(project="proj", extensions=[no_skeleton.NoSkeleton('no-skeleton')]) # when the project is created, create_project(opts) # then skeleton file should not exist assert not path_exists("proj/src/proj/skeleton.py") assert not path_exists("proj/tests/test_skeleton.py")
def test_create_project_with_empty_namespace(tmpfolder): for j, ns in enumerate(["", None, False]): # Given options with the namespace extension, opts = dict(project="my-proj{}".format(j), namespace=ns, extensions=[namespace.Namespace("namespace")]) # when the project is created, create_project(opts) # then plain structure should exist assert path_exists("my-proj{}/src/my_proj{}/__init__.py".format(j, j))
def test_create_project_no_cookiecutter(tmpfolder, nocookiecutter_mock): # Given options with the cookiecutter extension, # but without cookiecutter being installed, opts = dict(project=PROJ_NAME, cookiecutter=COOKIECUTTER_URL, extensions=[cookiecutter.Cookiecutter('cookiecutter')]) # when the project is created, # then an exception should be raised. with pytest.raises(cookiecutter.NotInstalled): create_project(opts)
def test_create_project_with_license(tmpfolder, git_mock): _, opts = get_default_options({}, dict( project="my-project", license="new-bsd")) # ^ The entire default options are needed, since template # uses computed information create_project(opts) assert path_exists("my-project") content = tmpfolder.join("my-project/LICENSE.txt").read() assert content == templates.license(opts)
def test_create_project_with_django(tmpfolder): # Given options with the django extension, opts = dict(project=PROJ_NAME, extensions=[django.Django('django')]) # when the project is created, create_project(opts) # then django files should exist for path in DJANGO_FILES: assert path_exists(path) # and also overwritable pyscaffold files (with the exact contents) tmpfolder.join(PROJ_NAME).join("setup.py").read() == setup_py(opts)
def test_cli_with_django_and_update(tmpfolder, capsys): # Given a project exists create_project(project=PROJ_NAME) # when the project is updated # with the django extension, sys.argv = ["pyscaffold", PROJ_NAME, "--update", "--django"] run() # then a warning should be displayed out, err = capsys.readouterr() assert all(warn in out + err for warn in ( 'external tools', 'not supported', 'will be ignored'))
def test_create_project_with_cookiecutter(tmpfolder): # Given options with the cookiecutter extension, opts = dict(project=PROJ_NAME, cookiecutter=COOKIECUTTER_URL, extensions=[cookiecutter.Cookiecutter('cookiecutter')]) # when the project is created, create_project(opts) # then cookiecutter files should exist for path in COOKIECUTTER_FILES: assert path_exists(path) # and also overwritable pyscaffold files (with the exact contents) tmpfolder.join(PROJ_NAME).join("setup.py").read() == setup_py(opts)
def test_pretend_create_project_with_django(tmpfolder, caplog): # Given options with the django extension, opts = parse_args([PROJ_NAME, '--pretend', '--django']) # when the project is created, create_project(opts) # then files should exist assert not path_exists(PROJ_NAME) for path in DJANGO_FILES: assert not path_exists(path) # but activities should be logged assert re.search(r'run\s+django', caplog.text)
def test_move_old_package_without_namespace(tmpfolder): # Given a package is already created without namespace create_project(project="proj", package="my_pkg") opts = dict(project="proj", package="my_pkg") struct = dict(proj={'src': {'my_pkg': {'file.py': ''}}}) # when no 'namespace' option is passed, struct, opts = get_default_options(struct, opts) struct, opts = enforce_namespace_options(struct, opts) struct, opts = move_old_package(struct, opts) # then the old package remains, assert tmpfolder.join("proj/src/my_pkg/__init__.py").check()
def test_create_project_with_namespace(tmpfolder): # Given options with the namespace extension, opts = dict(project="my-proj", namespace="ns.ns2", extensions=[namespace.Namespace('namespace')]) # when the project is created, create_project(opts) # then nested structure should exist assert path_exists("my-proj/src/ns/__init__.py") assert path_exists("my-proj/src/ns/ns2/__init__.py") assert path_exists("my-proj/src/ns/ns2/my_proj/__init__.py") # and plain structure should not exist assert not path_exists("my-proj/src/my_proj/__init__.py")
def test_create_project_cookiecutter_and_update(tmpfolder, capsys): # Given a project exists create_project(project=PROJ_NAME) # when the project is updated # with the cookiecutter extension, opts = dict(project=PROJ_NAME, update=True, cookiecutter=COOKIECUTTER_URL, extensions=[cookiecutter.Cookiecutter('cookiecutter')]) create_project(opts) # then a warning should be displayed out, err = capsys.readouterr() assert all(warn in out + err for warn in ( 'external tools', 'not supported', 'will be ignored'))
def test_pretend_create_project_with_cookiecutter(tmpfolder, caplog): # Given options with the cookiecutter extension, caplog.set_level(logging.INFO) opts = parse_args( [PROJ_NAME, '--pretend', '--cookiecutter', COOKIECUTTER_URL]) opts = process_opts(opts) # when the project is created, create_project(opts) # then files should exist assert not path_exists(PROJ_NAME) for path in COOKIECUTTER_FILES: assert not path_exists(path) # but activities should be logged logs = caplog.text assert re.search(r'run.+cookiecutter', logs)
def test_pretend_when_updating_does_not_make_changes(tmpfolder): # Given a project already exists opts = dict(project="proj", license="mit") create_project(opts) setup_changed = getmtime('proj/setup.cfg') license_changed = getmtime('proj/LICENSE.txt') # When it is updated with different configuration, create_project(project="proj", update=True, force=True, pretend=True, url="my.project.net", license="mozilla") # Then nothing should change assert getmtime('proj/setup.cfg') == setup_changed assert 'my.project.net' not in tmpfolder.join('proj/setup.cfg').read() assert getmtime('proj/LICENSE.txt') == license_changed assert 'MIT License' in tmpfolder.join('proj/LICENSE.txt').read()
def test_create_project_call_extension_hooks(tmpfolder, git_mock): # Given an extension with hooks, called = [] def pre_hook(struct, opts): called.append("pre_hook") return struct, opts def post_hook(struct, opts): called.append("post_hook") return struct, opts # when created project is called, create_project(project="proj", extensions=[ create_extension(pre_hook, post_hook) ]) # then the hooks should also be called. assert "pre_hook" in called assert "post_hook" in called
def test_create_project_with_pre_commit(tmpfolder, caplog): caplog.set_level(logging.WARNING) # Given options with the pre-commit extension, opts = dict(project="proj", extensions=[pre_commit.PreCommit('pre-commit')]) # when the project is created, create_project(opts) # then pre-commit files should exist assert path_exists("proj/.pre-commit-config.yaml") assert path_exists("proj/.isort.cfg") # and the user should be instructed to install pre-commit expected_warnings = ('to make sure the hooks will run', 'cd proj', 'pre-commit install') log = caplog.text for text in expected_warnings: assert text in log
def test_updating_existing_project(tmpfolder, caplog): # Given a project already exists, but was generated without # namespace, create_project(project="my-proj") assert tmpfolder.join("my-proj/src/my_proj").check() assert not tmpfolder.join("my-proj/src/my/ns").check() # when the project is updated with a namespace, create_project(project="my-proj", update=True, namespace="my.ns", extensions=[namespace.Namespace("namespace")]) # then the package folder should be moved to a nested position, assert not tmpfolder.join("my-proj/src/my_proj").check() assert tmpfolder.join("my-proj/src/my/ns/my_proj").check() # and the user should see a warn expected_warnings = ('A folder', 'exists in the project directory', 'a namespace option was passed', 'Please make sure') log = caplog.text for text in expected_warnings: assert text in log
def test_create_project_respect_update_rules(tmpfolder, git_mock): # Given an existing project opts = dict(project="proj") create_project(opts) for i in (0, 1, 3, 5, 6): tmpfolder.ensure("proj/tests/file"+str(i)).write("old") assert path_exists("proj/tests/file"+str(i)) # and an extension with extra files def add_files(struct, opts): print("inside opts", opts) nov, ncr = helpers.NO_OVERWRITE, helpers.NO_CREATE struct = helpers.ensure(struct, "proj/tests/file0", "new") struct = helpers.ensure(struct, "proj/tests/file1", "new", nov) struct = helpers.ensure(struct, "proj/tests/file2", "new", ncr) struct = helpers.merge(struct, { "proj": {"tests": {"file3": ("new", nov), "file4": ("new", ncr), "file5": ("new", None), "file6": "new"}} }) return struct, opts # When the created project is called, create_project(project="proj", update=True, extensions=[ create_extension(add_files) ]) # then the NO_CREATE files should not be created, assert not path_exists("proj/tests/file2") assert not path_exists("proj/tests/file4") # the NO_OVERWRITE files should not be updated assert tmpfolder.join("proj/tests/file1").read() == "old" assert tmpfolder.join("proj/tests/file3").read() == "old" # and files with no rules or `None` rules should be updated assert tmpfolder.join("proj/tests/file0").read() == "new" assert tmpfolder.join("proj/tests/file5").read() == "new" assert tmpfolder.join("proj/tests/file6").read() == "new"
def test_create_project_generate_extension_files(tmpfolder, git_mock): # Given a blank state, assert not path_exists("proj/tests/extra.file") assert not path_exists("proj/tests/another.file") # and an extension with extra files, def add_files(struct, opts): struct = helpers.ensure(struct, "proj/tests/extra.file", "content") struct = helpers.merge(struct, { "proj": {"tests": {"another.file": "content"}}}) return struct, opts # when the created project is called, create_project(project="proj", extensions=[ create_extension(add_files) ]) # then the files should be created assert path_exists("proj/tests/extra.file") assert tmpfolder.join("proj/tests/extra.file").read() == "content" assert path_exists("proj/tests/another.file") assert tmpfolder.join("proj/tests/another.file").read() == "content"