Skip to content

Commit

Permalink
feat: set initial style url(s) with nitpick init (#473)
Browse files Browse the repository at this point in the history
This allows for a project to set their initial style URLs with the command-line, rather than defaulting to the standard URL:

```sh
$ nitpick init http://example.com/style1 ./local/style2
```
  • Loading branch information
mjpieters authored Mar 26, 2022
1 parent ec934dc commit 0100f2b
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 22 deletions.
2 changes: 1 addition & 1 deletion docs/cli.rst
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ At the end of execution, this command displays:

.. code-block::
Usage: nitpick init [OPTIONS]
Usage: nitpick init [OPTIONS] [STYLE_URLS]...
Create a [tool.nitpick] section in the configuration file if it doesn't
exist already.
Expand Down
8 changes: 7 additions & 1 deletion docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@ The first file found will be used; the other files will be ignored.

Run the ``nipick init`` CLI command to create a config file (:ref:`cli_cmd_init`).

To configure your own style:
To configure your own style, you can either use ``nitpick init``:

.. code-block:: sh
$ nitpick init /path/to/your-style-file.toml
or edit your configuration file and set the ``style`` option:

.. code-block:: toml
Expand Down
7 changes: 4 additions & 3 deletions src/nitpick/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,14 +144,15 @@ def ls(context, files): # pylint: disable=invalid-name

@nitpick_cli.command()
@click.pass_context
def init(context):
@click.argument("style_urls", nargs=-1)
def init(context, style_urls):
"""Create a [tool.nitpick] section in the configuration file if it doesn't exist already."""
nit = get_nitpick(context)
config = nit.project.read_configuration()

if config.file and PROJECT_NAME in TomlDoc(path=config.file).as_object[TOOL_KEY]:
if config.file and PROJECT_NAME in TomlDoc(path=config.file).as_object.get(TOOL_KEY, {}):
click.secho(f"The config file {config.file.name} already has a [{TOOL_NITPICK_KEY}] section.", fg="yellow")
raise Exit(1)

nit.project.create_configuration(config)
nit.project.create_configuration(config, *style_urls)
click.secho(f"A [{TOOL_NITPICK_KEY}] section was created in the config file: {config.file.name}", fg="green")
21 changes: 12 additions & 9 deletions src/nitpick/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
from pathlib import Path
from typing import Iterable, Iterator

import tomlkit
from autorepr import autorepr
from loguru import logger
from marshmallow_polyfield import PolyField
from more_itertools import peekable
from more_itertools.more import always_iterable
from pluggy import PluginManager
from tomlkit import comment, document, dumps, parse, table
from tomlkit.items import KeyType, SingleKey
from tomlkit.toml_document import TOMLDocument

Expand Down Expand Up @@ -203,21 +203,24 @@ def merge_styles(self, offline: bool) -> Iterator[Fuss]:
self.nitpick_section = self.style_dict.get("nitpick", {})
self.nitpick_files_section = self.nitpick_section.get("files", {})

def create_configuration(self, config: Configuration) -> None:
def create_configuration(self, config: Configuration, *style_urls: str) -> None:
"""Create a configuration file."""
from nitpick.style import StyleManager # pylint: disable=import-outside-toplevel

if config.file:
doc: TOMLDocument = parse(config.file.read_text())
doc: TOMLDocument = tomlkit.parse(config.file.read_text())
else:
doc = document()
doc = tomlkit.document()
config.file = self.root / DOT_NITPICK_TOML

tool_nitpick = table()
tool_nitpick.add(comment("Generated by the 'nitpick init' command"))
tool_nitpick.add(comment(f"More info at {READ_THE_DOCS_URL}configuration.html"))
tool_nitpick.add("style", [str(StyleManager.get_default_style_url())])
if not style_urls:
style_urls = (str(StyleManager.get_default_style_url()),)

tool_nitpick = tomlkit.table()
tool_nitpick.add(tomlkit.comment("Generated by the 'nitpick init' command"))
tool_nitpick.add(tomlkit.comment(f"More info at {READ_THE_DOCS_URL}configuration.html"))
tool_nitpick.add("style", tomlkit.array([tomlkit.string(url) for url in style_urls]))
doc.add(SingleKey(TOOL_NITPICK_KEY, KeyType.Bare), tool_nitpick)

# config.file will always have a value at this point, but mypy can't see it.
config.file.write_text(dumps(doc, sort_keys=True)) # type: ignore
config.file.write_text(tomlkit.dumps(doc, sort_keys=True)) # type: ignore
4 changes: 2 additions & 2 deletions tests/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -399,9 +399,9 @@ def cli_ls(self, str_or_lines: StrOrList, *, exit_code: int = None) -> ProjectMo
compare(actual=actual, expected=expected, prefix=f"Result: {result}")
return self

def cli_init(self, str_or_lines: StrOrList, *, exit_code: int = None) -> ProjectMock:
def cli_init(self, str_or_lines: StrOrList, *args, exit_code: int = None) -> ProjectMock:
"""Run the init command and assert the output."""
result, actual, expected = self._simulate_cli("init", str_or_lines, exit_code=exit_code)
result, actual, expected = self._simulate_cli("init", str_or_lines, *args, exit_code=exit_code)
compare(actual=actual, expected=expected, prefix=f"Result: {result}")
return self

Expand Down
39 changes: 33 additions & 6 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""CLI tests."""
import pytest
import tomlkit

from nitpick.constants import DOT_NITPICK_TOML, PYPROJECT_TOML, READ_THE_DOCS_URL, TOOL_NITPICK_KEY
from nitpick.style import StyleManager
Expand Down Expand Up @@ -59,23 +60,50 @@ def test_create_basic_dot_nitpick_toml(tmp_path):
[{TOOL_NITPICK_KEY}]
# Generated by the 'nitpick init' command
# More info at {READ_THE_DOCS_URL}configuration.html
style = ["{url}"]
style = ['{url}']
""",
)
assert url.scheme == Scheme.PY


def test_add_tool_nitpick_section_to_pyproject_toml(tmp_path):
def test_init_empty_pyproject_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.pyproject_toml("").cli_init(
f"A [{TOOL_NITPICK_KEY}] section was created in the config file: {PYPROJECT_TOML}"
).assert_file_contents(
PYPROJECT_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


@pytest.mark.parametrize(
"styles",
[
(), # no arguments, default style
("https://github.com/andreoliwa/nitpick/blob/develop/initial.toml", "./local.toml"),
],
)
def test_add_tool_nitpick_section_to_pyproject_toml(tmp_path, styles):
"""Add a [tool.nitpick] section to pyproject.toml."""
project = ProjectMock(tmp_path).pyproject_toml(
"""
[tool.black]
line-length = 120
"""
)
url = StyleManager.get_default_style_url()
expected = styles or [StyleManager.get_default_style_url()]

project.cli_init(
f"A [{TOOL_NITPICK_KEY}] section was created in the config file: {PYPROJECT_TOML}"
f"A [{TOOL_NITPICK_KEY}] section was created in the config file: {PYPROJECT_TOML}", *styles
).assert_file_contents(
PYPROJECT_TOML,
f"""
Expand All @@ -85,7 +113,6 @@ def test_add_tool_nitpick_section_to_pyproject_toml(tmp_path):
[{TOOL_NITPICK_KEY}]
# Generated by the 'nitpick init' command
# More info at {READ_THE_DOCS_URL}configuration.html
style = ["{url}"]
style = {tomlkit.array([tomlkit.string(str(url)) for url in expected]).as_string()}
""",
)
assert url.scheme == Scheme.PY

0 comments on commit 0100f2b

Please sign in to comment.