ruff icon indicating copy to clipboard operation
ruff copied to clipboard

[Feature Request / Proposal] Upstream / preset / profile to preconfigure options

Open pwoolvett opened this issue 3 years ago • 13 comments

Would something like preset or a profile be a feature you'd like to see?

for example:

[tool.ruff]
preset = "charliemarsh"

alternatively

[tool.ruff]
upstream = "https://.../ruff-profile.toml"

The idea would be to (1) add a new parameter in the cli and in the config , and (2) override defaults eg after this with the loaded contents of the profile / upstream file.

would a PR with mentioned implementation be accepted? Or would more discussion be required (eg as part of the eventual plugin system)? Or maybe it's just a non-goal?

pwoolvett avatar Nov 18 '22 18:11 pwoolvett

Yeah I like this idea, I think this could be quite useful. The main question that comes to mind is where / how the presets should be defined.

isort supports presets (called "profiles"), but they're all defined in isort itself, which is probably not what we want here.

eslint supports shareable configs, but they have to be released on npm (or defined and loaded from the local filesystem), which we also probably don't want.

A URL seems reasonable, but we'd definitely want to cache it.

Can we think of other examples to model on here? Maybe something pre-commit like, where you specify a URL and explicitly run install to fetch (or update) the config locally?

charliermarsh avatar Nov 18 '22 19:11 charliermarsh

Presets seem to involve more logic.

I think the most straightforward would be the "upstream" variant, which would just be a local or cached remote .toml with configuration to use as defaults, overriden by local ruff config+cli. Then most common "presets" could just be tomls hosted together with the documentation :).

In that case, the pre-commit hook could be used to invalidate cached url (stage 1), before actually running ruff (stage 2). Or actually just provide two ruff hooks here: one to invalidate remote (people not using upstream wont care about this), and the current one. (you were talking about pre-commit, not pre-commit, right?)

pwoolvett avatar Nov 18 '22 19:11 pwoolvett

Yeah. We could also just cache it in .ruff_cache and then users can always rm -rf .ruff_cache to clear it out. (I want to make regular purging of the .ruff_cache slightly more automatic anyway, right now it just gets bigger with every release.)

charliermarsh avatar Nov 18 '22 19:11 charliermarsh

@pwoolvett - Are you interested in working on it? :)

charliermarsh avatar Nov 18 '22 23:11 charliermarsh

sure! ill have a go at it and send a pr

pwoolvett avatar Nov 20 '22 00:11 pwoolvett

We now have extend = "/path/to/pyproject.toml" which is relevant here.

charliermarsh avatar Dec 16 '22 05:12 charliermarsh

Maybe relevant, Something akin to tsconfig.json extends?

https://www.typescriptlang.org/tsconfig#extends

michaeloliverx avatar Feb 07 '23 17:02 michaeloliverx

Created a Discussion around this: https://github.com/charliermarsh/ruff/discussions/3363

charliermarsh avatar Mar 06 '23 17:03 charliermarsh

We now have extend = "/path/to/pyproject.toml" which is relevant here.

The more projects I move over to Ruff, the more I really wish I could extend some base configs cross-projects. Something like being able to use a URL in extend or a path relative to site-packages, like:

Maybe relevant, Something akin to tsconfig.json extends?

typescriptlang.org/tsconfig#extends

It's also the last major feature missing before I feel confident showing off (and integrating) Ruff at my workplace, where we love being able to reference a global base configuration for our tooling, massively simplifying other dev's work when it comes to following company-wide standards, staying up to date, and integrating/configuring the tools in all projects as easily as possible.

(tl;dr: adoption is easier when I can say "add these 3 lines to your configs" instead of "copy these config files that will soon be out of date")

Avasam avatar Apr 12 '23 15:04 Avasam

@charliermarsh we spoke about this at PyCon how for Python would generalize better if one could pull configuration via a plugin system of an additionaly installed Python package. Ideally, we'd not even need any configuration such as:

[tool.ruff]
preset = "tox_ruff_config"

When installed into a Python interpreter, we could just use https://docs.python.org/3/library/importlib.metadata.html#entry-points via the ruff key, and if a such library is detected load that configuration file as a base (still overwrite-able via pyproject.toml). How one woud use this is via pre-commit:

- repo: https://github.com/charliermarsh/ruff-pre-commit
  rev: "v0.0.263"
  hooks:
    - id: ruff
       additional_dependencies: [tox_ruff_config==1.0.1]

This solution would cache natively, would be versioned by design, and would work behind enterprise firewalls too. This feature would only work if ruff is installed via Python, and would be no-op should you run ruff as a dedicated binary.

gaborbernat avatar Apr 26 '23 21:04 gaborbernat

It would be nice to be able to extend the config from a main project on another git.

[tool.ruff]
extend = "../ruff/pyproject.toml"
# instead we could write something like this :
extend = { git = "https://github.com/astral-sh/ruff.git", tag="0.2.1" }
extend = { git = "https://github.com/astral-sh/ruff.git", branch="some_branch" }
# maybe with the possibility to configure the path to the `pyproject.toml`
extend = { git = "https://github.com/astral-sh/ruff.git", tag="0.2.1", path = "my_dir_configs/pyproject.toml" }

Could even imagine to retrieve a config from a dependency manager like pep compliant or poetry :

[project]
dependencies = [
    "my-repo @ git+https://github.com/my-repo/[email protected]"
]
[tool.poetry.dependencies]
my-repo = { git = "https://github.com/my-repo/ruff.git", tag="0.2.1" }

[tool.ruff]
extend = { pep = "my-repo", path = "my_dir_configs/pyproject.toml" }
extend = { poetry = "my-repo", path = "my_dir_configs/pyproject.toml" }

The logic behind will basically do something like this :

# With default values :
# extend_tag=""
# extend_branch=""
# extend_path="pyproject.toml"
position=`pwd`
git clone --no-checkout --depth=1 --no-tags $extend_git $somewhere_in_ruff_cache
cd $somewhere_in_ruff_cache
if [ "$extend_tag" != "" ]; then
    git fetch origin "+refs/tags/${extend_tag}:refs/tags/${extend_tag}" --no-tags
    git checkout $extend_tag -- $extend_path
else
    git checkout $extend_branch -- $extend_path  # empty branch is HEAD
fi
cd $position
# config ruff to point on the retrieved toml
config_extend "${somewhere_in_ruff_cache}/${extend_path}"

Then we could imagine that extend can include presets :

extend = { preset = "strict_without_doc" }
# Would be be an alias for :
extend = { git = "https://github.com/astral-sh/ruff.git", tag = "<running_ruff_version>", path = "presets_config/strict_without_doc.toml" }
# We could even imagine to block the config to an older version
extend = { preset = "strict_without_doc", tag = "0.2.0" }

I think it is better to keep the same key for adding other configs. So everything that involves joining a toml would be under extend, doc, config and code would be clearer this way. Then to have multiple configs, we could image a list, where order matters :

extend = [
    { preset = "Doc_permissive" },
    { preset = "Flake8_without_warnings" },
    { preset = "Import" },
    { preset = "Security_strict" },
]

yoann9344 avatar Feb 19 '24 23:02 yoann9344

Also very interested in the feature to extend from a remote URL. Use-case is to settle on a sane base config for ruff which can get included in all projects, but individual rules could easly tuned without code duplication.

hinricht avatar Apr 18 '24 07:04 hinricht

Do keep in mind though that online shared configs will break pre-commit.ci as it doesn't allow internet acces whilst running, only on install.

This could be worked around if the pre-commit action can "dry-run" and cache the extended config file.

dprint currently has this issue.

Avasam avatar Apr 18 '24 08:04 Avasam