Example #1
0
def test_include_remote_style_from_local_style(tmp_path):
    """Test include of remote style when there is only a local style."""
    remote_style = "https://raw.githubusercontent.com/user/repo/branch/path/to/nitpick-style"
    url_with_extension = f"{remote_style}{TOML_EXTENSION}"
    body = """
        ["tox.ini".section]
        key = "value"
    """
    responses.add(responses.GET, url_with_extension, dedent(body), status=200)

    project = ProjectMock(tmp_path).style(f"""
        [nitpick.styles]
        include = [
            "{remote_style}"
        ]
        """)
    project.assert_file_contents(TOX_INI, None).api_check_then_fix(
        Fuss(True, TOX_INI, 321,
             " was not found. Create it with this content:",
             "[section]\nkey = value")).assert_file_contents(
                 TOX_INI,
                 """
        [section]
        key = value
        """,
                 PYPROJECT_TOML,
                 None,
             )
Example #2
0
def project_remote(request, tmp_path):
    """Project with a remote style (loaded from a URL)."""
    from tests.helpers import ProjectMock, tomlstring

    remote_url = "https://example.com/remote-style.toml"
    remote_style = """
        ["pyproject.toml".tool.black]
        line-length = 100
    """
    # https://docs.pytest.org/en/stable/fixture.html#using-markers-to-pass-data-to-fixtures
    marker = request.node.get_closest_marker("tool_nitpick")
    tool_nitpick = marker.args[0] if marker else ""

    with RequestsMock() as mocked_response:
        mocked_response.add(mocked_response.GET,
                            remote_url,
                            dedent(remote_style),
                            status=200)

        project = ProjectMock(tmp_path)
        project.pyproject_toml(f"""
            [tool.nitpick]
            style = {tomlstring(remote_url)}
            {tool_nitpick}

            [tool.black]
            line-length = 100
            """).remote(mocked_response, remote_url)
        yield project
Example #3
0
def test_relative_style_on_urls(tmp_path):
    """Read styles from relative paths on URLs."""
    base_url = "http://www.example.com/sub/folder"
    mapping = {
        "main":
        """
            [nitpick.styles]
            include = "presets/python.toml"
            """,
        "presets/python":
        """
            [nitpick.styles]
            include = [
                "../styles/pytest.toml",
                "../styles/black.toml",
            ]
            """,
        "styles/pytest":
        """
            ["pyproject.toml".tool.pytest]
            some-option = 123
            """,
        "styles/black":
        """
            ["pyproject.toml".tool.black]
            line-length = 99
            missing = "value"
            """,
    }
    for filename, body in mapping.items():
        responses.add(responses.GET,
                      f"{base_url}/{filename}.toml",
                      dedent(body),
                      status=200)

    project = ProjectMock(tmp_path)

    common_pyproject = """
        [tool.black]
        line-length = 99
        [tool.pytest]
        some-option = 123
    """
    # Use full path on initial styles
    project.pyproject_toml(f"""
        [tool.nitpick]
        style = ["{base_url}/main"]
        {common_pyproject}
        """).api_check().assert_violations(
        Fuss(
            False,
            PYPROJECT_TOML,
            318,
            " has missing values:",
            """
            [tool.black]
            missing = "value"
            """,
        ))
Example #4
0
def test_no_python_file_root_dir(request):
    """No Python file on the root dir."""
    project = ProjectMock(request,
                          setup_py=False).pyproject_toml("").save_file(
                              "whatever.sh", "", lint=True).flake8()
    project.assert_single_error(
        "NIP102 No Python file was found on the root dir and subdir of {!r}".
        format(str(project.root_dir)))
Example #5
0
def test_no_root_dir_with_python_file(tmp_path, shared_datadir):
    """No root dir with Python file."""
    hello_py = tmp_path / "hello.py"
    shutil.copy(shared_datadir / "hello.py", hello_py)
    project = ProjectMock(tmp_path, pyproject_toml=False, setup_py=False)
    project.files_to_lint.append(hello_py)
    error = f"NIP101 {ProjectViolations.NO_ROOT_DIR.message}"
    project.flake8().assert_single_error(error).cli_run(
        error, exit_code=2).cli_ls(error, exit_code=2)
Example #6
0
def test_protocol_not_supported(tmp_path):
    """Test unsupported protocols."""
    project = ProjectMock(tmp_path).pyproject_toml("""
        [tool.nitpick]
        style = ["abc://www.example.com/style.toml"]
        """)
    with pytest.raises(RuntimeError) as exc_info:
        project.api_check()
    assert str(exc_info.value) == "URL protocol 'abc' is not supported"
Example #7
0
def test_config_file_already_has_tool_nitpick_section(tmp_path, config_file):
    """Test if both config files already exist."""
    project = ProjectMock(tmp_path, pyproject_toml=False,
                          setup_py=True).save_file(
                              config_file,
                              f"""
        [{TOOL_NITPICK_KEY}]
        style = ['/this/should/not/be/validated-yet.toml']
        """,
                          )
    project.cli_init(
        f"The config file {config_file} already has a [{TOOL_NITPICK_KEY}] section.",
        exit_code=1)
Example #8
0
def test_each_builtin_style(tmp_path, datadir, builtin_style_path):
    """Test each built-in style (skip presets)."""
    style = BuiltinStyle.from_path(builtin_style_path)
    violations = []
    name_contents = []
    for filename in style.files:
        expected_path = datadir / style.path_from_resources_root / filename

        if not expected_path.exists():
            # Creates empty files on datadir, to help with the task of adding new built-in styles
            # You just need to fill in the expected contents of each file
            fixture_path = Path(
                __file__
            ).parent / "test_builtin" / style.path_from_resources_root / filename
            fixture_path.parent.mkdir(parents=True, exist_ok=True)
            fixture_path.touch(exist_ok=True)

        expected_contents = expected_path.read_text()
        code = BUILTIN_STYLE_CODES[filename]
        violations.append(
            Fuss(True, filename, code,
                 " was not found. Create it with this content:",
                 expected_contents))
        name_contents.extend([filename, expected_contents])

    violations.extend(
        BUILTIN_STYLE_EXTRA_VIOLATIONS.get(style.path_from_resources_root, []))

    project = ProjectMock(tmp_path).save_file(
        DOT_NITPICK_TOML,
        f"""
        [tool.nitpick]
        style = "{style.py_url_without_ext}"
        """,
    )

    # Run `nitpick fix` twice on the style
    # First time check: it should report violations and create new file(s)
    project.api_check_then_fix(*violations)
    if style.files:
        project.assert_file_contents(*name_contents)

    has_unfixed_violations = any(not fuss.fixed for fuss in violations)
    if has_unfixed_violations:
        # If some violations can't be fixed, we can't check for the second time and we must leave
        return

    # Second time check: it should not report any violation and should not change the existing file(s)
    project.api_check_then_fix()
    if style.files:
        project.assert_file_contents(*name_contents)
Example #9
0
def test_has_one_config_file(tmp_path, config_file, caplog):
    """There is a root dir (setup.py) and a single config file."""
    project = ProjectMock(tmp_path, pyproject_toml=False, setup_py=True)
    project.save_file("local.toml", "").save_file(
        config_file,
        """
        [tool.nitpick]
        style = ["local.toml"]
        cache = "forever"
        """,
    ).api_check(offline=True)
    path = project.root_dir / config_file
    assert project.nitpick_instance.project.read_configuration(
    ) == Configuration(path, ["local.toml"], "forever")
    assert f"Config file: reading from {path}" in caplog.text
Example #10
0
def test_suggest_initial_contents(request):
    """Suggest initial contents for missing pre-commit config file."""
    ProjectMock(request).load_styles("isort", "black").pyproject_toml("""
        [tool.nitpick]
        style = ["isort", "black"]
        """).flake8().assert_errors_contain("""
        NIP331 File .pre-commit-config.yaml was not found. Create it with this content:\x1b[32m
        repos:
          - repo: https://github.com/asottile/seed-isort-config
            rev: v1.9.3
            hooks:
              - id: seed-isort-config
          - repo: https://github.com/pre-commit/mirrors-isort
            rev: v4.3.21
            hooks:
              - id: isort
          - repo: https://github.com/python/black
            rev: 19.10b0
            hooks:
              - id: black
                args: [--safe, --quiet]
          - repo: https://github.com/asottile/blacken-docs
            rev: v1.3.0
            hooks:
              - id: blacken-docs
                additional_dependencies: [black==19.10b0]\x1b[0m
        """)
Example #11
0
def test_invalid_nitpick_files(offline, tmp_path):
    """Invalid [nitpick.files] section."""
    ProjectMock(tmp_path).named_style(
        "some_style",
        """
        [xxx]
        wrong = "section"
        """,
    ).named_style(
        "wrong_files",
        """
        [nitpick.files.whatever]
        wrong = "section"
        """,
    ).pyproject_toml("""
        [tool.nitpick]
        style = ["some_style", "wrong_files"]
        """).flake8(offline=offline).assert_errors_contain(f"""
        NIP001 File some_style.toml has an incorrect style. Invalid config:{SUGGESTION_BEGIN}
        xxx: Unknown file. See {READ_THE_DOCS_URL}plugins.html.{SUGGESTION_END}
        """).assert_errors_contain(
        f"""
        NIP001 File wrong_files.toml has an incorrect style. Invalid config:{SUGGESTION_BEGIN}
        nitpick.files.whatever: Unknown file. See {READ_THE_DOCS_URL}nitpick_section.html#nitpick-files.{SUGGESTION_END}
        """,
        2,
    )
Example #12
0
def test_missing_hook_with_id(request):
    """Test missing hook with specific id."""
    ProjectMock(request).style(
        '''
        [["pre-commit-config.yaml".repos]]
        repo = "other"
        hooks = """
        - id: black
          name: black
          entry: black
        """
        '''
    ).pre_commit(
        """
        repos:
        - repo: other
          hooks:
          - id: isort
        """
    ).lint().assert_single_error(
        """
        NIP337 File .pre-commit-config.yaml: missing hook with id 'black':
          - id: black
            name: black
            entry: black
        """
    )
Example #13
0
def test_style_missing_id_in_hook(request):
    """Test style file is missing id in hook."""
    ProjectMock(request).style(
        '''
        [["pre-commit-config.yaml".repos]]
        repo = "another"
        hooks = """
        - name: isort
          entry: isort -sp setup.cfg
        """
        '''
    ).pre_commit(
        """
        repos:
        - repo: another
          hooks:
          - id: isort
        """
    ).lint().assert_single_error(
        """
        NIP336 File .pre-commit-config.yaml: style file is missing 'id' in hook:
            name: isort
            entry: isort -sp setup.cfg
        """
    )
Example #14
0
def test_simulate_parsing_error_when_saving(update_file, tmp_path):
    """Simulate a parsing error when saving an INI file."""
    update_file.side_effect = ParsingError(
        source="simulating a captured error")

    original_file = """
        [flake8]
        existing = value
        """
    ProjectMock(tmp_path).style(f"""
        ["{SETUP_CFG}".flake8]
        new = "value"
        """).setup_cfg(original_file).api_fix().assert_violations(
        Fuss(
            True,
            SETUP_CFG,
            324,
            ": section [flake8] has some missing key/value pairs. Use this:",
            """
            [flake8]
            new = value
            """,
        ),
        Fuss(
            False,
            SETUP_CFG,
            Violations.PARSING_ERROR.code,
            ": parsing error (ParsingError): Source contains parsing errors: 'simulating a captured error'",
        ),
    ).assert_file_contents(SETUP_CFG, original_file)
Example #15
0
def test_root_values_on_existing_file(request):
    """Test values on the root of the config file when there is a file."""
    ProjectMock(request).style(
        """
        ["pre-commit-config.yaml"]
        fail_fast = true
        blabla = "what"
        something = true
        another_thing = "yep"
        """
    ).pre_commit(
        """
        repos:
        - hooks:
          - id: whatever
        something: false
        another_thing: "nope"
        """
    ).lint().assert_errors_contain_unordered(
        """
        NIP338 File .pre-commit-config.yaml has missing values:\x1b[92m
        blabla: what
        fail_fast: true\x1b[0m
        """
    ).assert_errors_contain(
        """
        NIP339 File .pre-commit-config.yaml has different values. Use this:\x1b[92m
        another_thing: yep
        something: true\x1b[0m
        """
    )
Example #16
0
def test_when_no_config_file_the_default_style_is_requested(tmp_path, caplog):
    """There is a root dir (setup.py), but no config file."""
    project = ProjectMock(tmp_path, pyproject_toml=False,
                          setup_py=True).api_check(offline=True)
    assert project.nitpick_instance.project.read_configuration(
    ) == Configuration(None, [], "")
    assert "Config file: none found" in caplog.text
Example #17
0
def test_suggest_initial_contents(request):
    """Suggest contents when setup.cfg does not exist."""
    ProjectMock(request).style(
        """
        [nitpick.files."setup.cfg"]
        "missing_message" = "Do something here"

        ["setup.cfg".mypy]
        ignore_missing_imports = true

        ["setup.cfg".isort]
        line_length = 120

        ["setup.cfg".flake8]
        max-line-length = 120
        """
    ).lint().assert_single_error(
        """
        NIP321 File setup.cfg was not found. Do something here. Create it with this content:\x1b[92m
        [flake8]
        max-line-length = 120

        [isort]
        line_length = 120

        [mypy]
        ignore_missing_imports = True\x1b[0m
        """
    )
def test_suggest_initial_contents(tmp_path, datadir):
    """Suggest initial contents for missing pre-commit config file."""
    warnings.simplefilter("ignore")  # "repos.yaml" key
    ProjectMock(tmp_path).named_style(
        "isort", datadir / "1-isort.toml").named_style(
            "black", datadir / "1-black.toml").pyproject_toml("""
        [tool.nitpick]
        style = ["isort", "black"]
        """).api_check_then_fix(
                Fuss(
                    True,
                    PRE_COMMIT_CONFIG_YAML,
                    361,
                    " was not found. Create it with this content:",
                    """
            repos: []
            """,
                ),
                partial_names=[PRE_COMMIT_CONFIG_YAML],
            ).assert_file_contents(
                PRE_COMMIT_CONFIG_YAML,
                """
        repos: []
        """,
            )
Example #19
0
def test_minimum_version(mocked_version, offline, request):
    """Stamp a style file with a minimum required version, to indicate new features or breaking changes."""
    assert_conditions(mocked_version == "0.5.3")
    ProjectMock(request).named_style(
        "parent",
        """
        [nitpick.styles]
        include = "child.toml"
        ["pyproject.toml".tool.black]
        line-length = 100
        """,
    ).named_style(
        "child",
        """
        [nitpick]
        minimum_version = "1.0"
        """,
    ).pyproject_toml("""
        [tool.nitpick]
        style = "parent"
        [tool.black]
        line-length = 100
        """).flake8(offline=offline).assert_single_error(
        "NIP203 The style file you're using requires nitpick>=1.0 (you have 0.5.3). Please upgrade"
    )
def test_missing_hook_with_id(tmp_path):
    """Test missing hook with specific id."""
    ProjectMock(tmp_path).style('''
        [[".pre-commit-config.yaml".repos]]
        repo = "other"
        hooks = """
        - id: black
          name: black
          entry: black
        """
        ''').pre_commit("""
        repos:
        - repo: other
          hooks:
          - id: isort
        """).api_check_then_fix(
        Fuss(
            True,
            PRE_COMMIT_CONFIG_YAML,
            368,
            " has missing values:",
            """
            repos:
              - repo: other
                hooks: "- id: black\\n  name: black\\n  entry: black\\n"
            """,
        ))
def test_style_missing_id_in_hook(tmp_path):
    """Test style file is missing id in hook. Read the warning on :py:class:`nitpick.plugins.yaml.YamlPlugin`."""
    ProjectMock(tmp_path).style(f'''
        [[".pre-commit-config.yaml".repos]]
        repo = "another"
        hooks = """
        - name: isort
          entry: isort -sp {SETUP_CFG}
        """
        ''').pre_commit("""
        repos:
        - repo: another
          hooks:
          - id: isort
        """).api_check_then_fix(
        Fuss(
            True,
            PRE_COMMIT_CONFIG_YAML,
            368,
            " has missing values:",
            'repos:\n  - repo: another\n    hooks: "- name: isort\\n  entry: isort -sp setup.cfg\\n"',
        )).assert_file_contents(
            PRE_COMMIT_CONFIG_YAML,
            r"""
        repos:
          - repo: another
            hooks:
              - id: isort
          - repo: another
            hooks: "- name: isort\n  entry: isort -sp setup.cfg\n"
        """,
        )
def test_missing_repo_key(tmp_path):
    """Test missing repo key on the style file."""
    ProjectMock(tmp_path).style("""
        [[".pre-commit-config.yaml".repos]]
        grepo = "glocal"
        """).pre_commit("""
        repos:
        - hooks:
          - id: whatever
        """).api_check_then_fix(
        Fuss(
            True,
            PRE_COMMIT_CONFIG_YAML,
            368,
            " has missing values:",
            """
            repos:
              - grepo: glocal
            """,
        ), ).assert_file_contents(
            PRE_COMMIT_CONFIG_YAML,
            """
        repos:
          - hooks:
              - id: whatever
          - grepo: glocal
        """,
        )
Example #23
0
def test_comma_separated_keys_on_style_file(tmp_path):
    """Comma separated keys on the style file."""
    ProjectMock(tmp_path).style(f"""
        [nitpick.files."{SETUP_CFG}"]
        comma_separated_values = ["food.eat", "food.drink"]

        ["{SETUP_CFG}".food]
        eat = "salt,ham,eggs"
        drink = "water,bier,wine"
        """).setup_cfg("""
        [food]
        eat = spam,eggs,cheese
        drink =   wine , bier , water
        """).api_check_then_fix(
        Fuss(
            True,
            SETUP_CFG,
            Violations.MISSING_VALUES_IN_LIST.code,
            " has missing values in the 'eat' key. Include those values:",
            """
            [food]
            eat = (...),ham,salt
            """,
        )).assert_file_contents(
            SETUP_CFG,
            """
        [food]
        eat = spam,eggs,cheese,ham,salt
        drink =   wine , bier , water
        """,
        )
Example #24
0
def test_missing_sections(request):
    """Test missing sections."""
    ProjectMock(request).setup_cfg(
        """
        [mypy]
        ignore_missing_imports = true
        """
    ).style(
        """
        ["setup.cfg".mypy]
        ignore_missing_imports = true

        ["setup.cfg".isort]
        line_length = 120

        ["setup.cfg".flake8]
        max-line-length = 120
        """
    ).lint().assert_single_error(
        """
        NIP321 File setup.cfg has some missing sections. Use this:\x1b[92m
        [flake8]
        max-line-length = 120

        [isort]
        line_length = 120\x1b[0m
        """
    )
Example #25
0
def test_invalid_configuration_comma_separated_values(tmp_path):
    """Test an invalid configuration for comma_separated_values."""
    ProjectMock(tmp_path).style(f"""
        ["{SETUP_CFG}".flake8]
        max-line-length = 85
        max-complexity = 12
        ignore = "D100,D101,D102,D103,D104,D105,D106,D107,D202,E203,W503"
        select = "E241,C,E,F,W,B,B9"

        [nitpick.files."{SETUP_CFG}"]
        comma_separated_values = ["flake8.ignore", "flake8.exclude"]
        """).api_check().assert_violations(
        Fuss(
            False,
            SETUP_CFG,
            321,
            " was not found. Create it with this content:",
            """
            [flake8]
            max-line-length = 85
            max-complexity = 12
            ignore = D100,D101,D102,D103,D104,D105,D106,D107,D202,E203,W503
            select = E241,C,E,F,W,B,B9
            """,
        ))
Example #26
0
def test_comma_separated_keys_on_style_file(request):
    """Comma separated keys on the style file."""
    project = (
        ProjectMock(request)
        .style(
            """
            [nitpick.files."setup.cfg"]
            comma_separated_values = ["food.eat"]

            ["setup.cfg".food]
            eat = "salt,ham,eggs"
            """
        )
        .setup_cfg(
            """
            [food]
            eat = spam,eggs,cheese
            """
        )
        .lint()
    )
    project.assert_single_error(
        """
        NIP322 File setup.cfg has missing values in the 'eat' key. Include those values:\x1b[92m
        [food]
        eat = (...),ham,salt\x1b[0m
        """
    )
Example #27
0
def test_suggest_initial_contents(request):
    """Suggest contents when setup.cfg does not exist."""
    ProjectMock(request).style("""
        [nitpick.files.present]
        "setup.cfg" = "Do something here"

        ["setup.cfg".mypy]
        ignore_missing_imports = true

        ["setup.cfg".isort]
        line_length = 120

        ["setup.cfg".flake8]
        max-line-length = 120
        """).flake8().assert_errors_contain(
        """
        NIP321 File setup.cfg was not found. Create it with this content:\x1b[32m
        [flake8]
        max-line-length = 120

        [isort]
        line_length = 120

        [mypy]
        ignore_missing_imports = True\x1b[0m
        """,
        2,
    ).assert_errors_contain(
        "NIP103 File setup.cfg should exist: Do something here")
Example #28
0
def test_create_basic_dot_nitpick_toml(tmp_path):
    """If no config file is found, create a basic .nitpick.toml."""
    project = ProjectMock(tmp_path, pyproject_toml=False, setup_py=True)
    url = StyleManager.get_default_style_url()
    project.cli_init(
        f"A [{TOOL_NITPICK_KEY}] section was created in the config file: {DOT_NITPICK_TOML}"
    ).assert_file_contents(
        DOT_NITPICK_TOML,
        f"""
        [{TOOL_NITPICK_KEY}]
        # Generated by the 'nitpick init' command
        # More info at {READ_THE_DOCS_URL}configuration.html
        style = ['{url}']
        """,
    )
    assert url.scheme == Scheme.PY
Example #29
0
def test_invalid_nitpick_files(offline, request):
    """Invalid [nitpick.files] section."""
    ProjectMock(request).named_style(
        "some_style",
        """
        [xxx]
        wrong = "section"
        """,
    ).named_style(
        "wrong_files",
        """
        [nitpick.files.whatever]
        wrong = "section"
        """,
    ).pyproject_toml("""
        [tool.nitpick]
        style = ["some_style", "wrong_files"]
        """).flake8(offline=offline).assert_errors_contain("""
        NIP001 File some_style.toml has an incorrect style. Invalid config:\x1b[32m
        xxx: Unknown file. See https://nitpick.rtfd.io/en/latest/config_files.html.\x1b[0m
        """).assert_errors_contain(
        """
        NIP001 File wrong_files.toml has an incorrect style. Invalid config:\x1b[32m
        nitpick.files.whatever: Unknown file. See {}nitpick_section.html#nitpick-files.\x1b[0m
        """.format(READ_THE_DOCS_URL),
        2,
    )
Example #30
0
def test_different_missing_keys(request):
    """Test different and missing keys."""
    ProjectMock(request).setup_cfg(
        """
        [mypy]
        ignore_missing_imports = true
        [isort]
        line_length = 30
        [flake8]
        xxx = "aaa"
        """
    ).style(
        """
        ["setup.cfg".mypy]
        ignore_missing_imports = true

        ["setup.cfg".isort]
        line_length = 110

        ["setup.cfg".flake8]
        max-line-length = 112
        """
    ).lint().assert_errors_contain(
        """
        NIP323 File setup.cfg: [isort]line_length is 30 but it should be like this:\x1b[92m
        [isort]
        line_length = 110\x1b[0m
        """
    ).assert_errors_contain(
        """
        NIP324 File setup.cfg: section [flake8] has some missing key/value pairs. Use this:\x1b[92m
        [flake8]
        max-line-length = 112\x1b[0m
        """
    )