Skip to content

Commit 02b6857

Browse files
authored
style: upgrade to use ruff (#567)
1 parent 16e660d commit 02b6857

20 files changed

+803
-321
lines changed

.pre-commit-config.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
repos:
2+
- repo: https://github.com/astral-sh/ruff-pre-commit
3+
rev: v0.12.0
4+
hooks:
5+
# Run the linter.
6+
- id: ruff
7+
# Run the formatter.
8+
- id: ruff-format

CONTRIBUTING.md

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,29 @@ a pull request.
77

88
Executing the tests:
99

10-
$ pip install -r requirements.txt
11-
$ pip install -e .
12-
$ flake8
13-
$ pytest
10+
$ uv venv
11+
$ uv pip install -r requirements.txt
12+
$ uv pip install -e .
13+
$ uv ruff check .
14+
$ uv format .
15+
$ uv run pytest
1416

1517
or with [tox](https://pypi.org/project/tox/) installed:
1618

1719
$ tox
1820

1921

22+
Use of pre-commit is recommended:
23+
24+
$ uv run precommit install
25+
26+
2027
Documentation is published with [mkdocs]():
2128

2229
```shell
23-
$ pip install -r requirements-docs.txt
24-
$ pip install -e .
25-
$ mkdocs serve
30+
$ uv pip install -r requirements-docs.txt
31+
$ uv pip install -e .
32+
$ uv run mkdocs serve
2633
```
2734

2835
Open http://127.0.0.1:8000/ to view the documentation locally.
29-

MANIFEST.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
include LICENSE *.md *.yml *.toml
1+
include LICENSE *.md *.yml *.yaml *.toml
22

33
include tox.ini
44
recursive-include docs *.md

Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ sdist: clean
2424
ls -l dist
2525

2626
test:
27-
pip install -e .
28-
flake8 .
29-
py.test tests/
27+
uv pip install -e .
28+
ruff check .
29+
pytest tests/
3030

3131
coverage:
3232
coverage run --source=dotenv --omit='*tests*' -m py.test tests/ -v --tb=native

requirements.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
black~=22.3.0
21
bumpversion
32
click
4-
flake8>=2.2.3
53
ipython
64
pytest-cov
75
pytest>=3.9
86
sh>=2
97
tox
108
twine
119
wheel
10+
ruff
11+
pre-commit

ruff.toml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[lint]
2+
select = [
3+
# pycodestyle
4+
"E4",
5+
"E7",
6+
"E9",
7+
8+
# Pyflakes
9+
"F",
10+
11+
# flake8-bugbear
12+
"B",
13+
14+
# iSort
15+
"I",
16+
17+
# flake8-builtins
18+
"A",
19+
]

src/dotenv/__init__.py

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
from typing import Any, Optional
22

3-
from .main import (dotenv_values, find_dotenv, get_key, load_dotenv, set_key,
4-
unset_key)
3+
from .main import dotenv_values, find_dotenv, get_key, load_dotenv, set_key, unset_key
54

65

76
def load_ipython_extension(ipython: Any) -> None:
87
from .ipython import load_ipython_extension
8+
99
load_ipython_extension(ipython)
1010

1111

@@ -21,29 +21,31 @@ def get_cli_string(
2121
Useful for converting a arguments passed to a fabric task
2222
to be passed to a `local` or `run` command.
2323
"""
24-
command = ['dotenv']
24+
command = ["dotenv"]
2525
if quote:
26-
command.append(f'-q {quote}')
26+
command.append(f"-q {quote}")
2727
if path:
28-
command.append(f'-f {path}')
28+
command.append(f"-f {path}")
2929
if action:
3030
command.append(action)
3131
if key:
3232
command.append(key)
3333
if value:
34-
if ' ' in value:
34+
if " " in value:
3535
command.append(f'"{value}"')
3636
else:
3737
command.append(value)
3838

39-
return ' '.join(command).strip()
39+
return " ".join(command).strip()
4040

4141

42-
__all__ = ['get_cli_string',
43-
'load_dotenv',
44-
'dotenv_values',
45-
'get_key',
46-
'set_key',
47-
'unset_key',
48-
'find_dotenv',
49-
'load_ipython_extension']
42+
__all__ = [
43+
"get_cli_string",
44+
"load_dotenv",
45+
"dotenv_values",
46+
"get_key",
47+
"set_key",
48+
"unset_key",
49+
"find_dotenv",
50+
"load_ipython_extension",
51+
]

src/dotenv/cli.py

Lines changed: 64 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,18 @@
33
import shlex
44
import sys
55
from contextlib import contextmanager
6-
from typing import Any, Dict, IO, Iterator, List, Optional
6+
from typing import IO, Any, Dict, Iterator, List, Optional
77

8-
if sys.platform == 'win32':
8+
if sys.platform == "win32":
99
from subprocess import Popen
1010

1111
try:
1212
import click
1313
except ImportError:
14-
sys.stderr.write('It seems python-dotenv is not installed with cli option. \n'
15-
'Run pip install "python-dotenv[cli]" to fix this.')
14+
sys.stderr.write(
15+
"It seems python-dotenv is not installed with cli option. \n"
16+
'Run pip install "python-dotenv[cli]" to fix this.'
17+
)
1618
sys.exit(1)
1719

1820
from .main import dotenv_values, set_key, unset_key
@@ -29,25 +31,37 @@ def enumerate_env() -> Optional[str]:
2931
cwd = os.getcwd()
3032
except FileNotFoundError:
3133
return None
32-
path = os.path.join(cwd, '.env')
34+
path = os.path.join(cwd, ".env")
3335
return path
3436

3537

3638
@click.group()
37-
@click.option('-f', '--file', default=enumerate_env(),
38-
type=click.Path(file_okay=True),
39-
help="Location of the .env file, defaults to .env file in current working directory.")
40-
@click.option('-q', '--quote', default='always',
41-
type=click.Choice(['always', 'never', 'auto']),
42-
help="Whether to quote or not the variable values. Default mode is always. This does not affect parsing.")
43-
@click.option('-e', '--export', default=False,
44-
type=click.BOOL,
45-
help="Whether to write the dot file as an executable bash script.")
39+
@click.option(
40+
"-f",
41+
"--file",
42+
default=enumerate_env(),
43+
type=click.Path(file_okay=True),
44+
help="Location of the .env file, defaults to .env file in current working directory.",
45+
)
46+
@click.option(
47+
"-q",
48+
"--quote",
49+
default="always",
50+
type=click.Choice(["always", "never", "auto"]),
51+
help="Whether to quote or not the variable values. Default mode is always. This does not affect parsing.",
52+
)
53+
@click.option(
54+
"-e",
55+
"--export",
56+
default=False,
57+
type=click.BOOL,
58+
help="Whether to write the dot file as an executable bash script.",
59+
)
4660
@click.version_option(version=__version__)
4761
@click.pass_context
4862
def cli(ctx: click.Context, file: Any, quote: Any, export: Any) -> None:
4963
"""This script is used to set, get or unset values from a .env file."""
50-
ctx.obj = {'QUOTE': quote, 'EXPORT': export, 'FILE': file}
64+
ctx.obj = {"QUOTE": quote, "EXPORT": export, "FILE": file}
5165

5266

5367
@contextmanager
@@ -66,53 +80,57 @@ def stream_file(path: os.PathLike) -> Iterator[IO[str]]:
6680
exit(2)
6781

6882

69-
@cli.command()
83+
@cli.command(name="list")
7084
@click.pass_context
71-
@click.option('--format', default='simple',
72-
type=click.Choice(['simple', 'json', 'shell', 'export']),
73-
help="The format in which to display the list. Default format is simple, "
74-
"which displays name=value without quotes.")
75-
def list(ctx: click.Context, format: bool) -> None:
85+
@click.option(
86+
"--format",
87+
"output_format",
88+
default="simple",
89+
type=click.Choice(["simple", "json", "shell", "export"]),
90+
help="The format in which to display the list. Default format is simple, "
91+
"which displays name=value without quotes.",
92+
)
93+
def list_values(ctx: click.Context, output_format: str) -> None:
7694
"""Display all the stored key/value."""
77-
file = ctx.obj['FILE']
95+
file = ctx.obj["FILE"]
7896

7997
with stream_file(file) as stream:
8098
values = dotenv_values(stream=stream)
8199

82-
if format == 'json':
100+
if output_format == "json":
83101
click.echo(json.dumps(values, indent=2, sort_keys=True))
84102
else:
85-
prefix = 'export ' if format == 'export' else ''
103+
prefix = "export " if output_format == "export" else ""
86104
for k in sorted(values):
87105
v = values[k]
88106
if v is not None:
89-
if format in ('export', 'shell'):
107+
if output_format in ("export", "shell"):
90108
v = shlex.quote(v)
91-
click.echo(f'{prefix}{k}={v}')
109+
click.echo(f"{prefix}{k}={v}")
92110

93111

94-
@cli.command()
112+
@cli.command(name="set")
95113
@click.pass_context
96-
@click.argument('key', required=True)
97-
@click.argument('value', required=True)
98-
def set(ctx: click.Context, key: Any, value: Any) -> None:
114+
@click.argument("key", required=True)
115+
@click.argument("value", required=True)
116+
def set_value(ctx: click.Context, key: Any, value: Any) -> None:
99117
"""Store the given key/value."""
100-
file = ctx.obj['FILE']
101-
quote = ctx.obj['QUOTE']
102-
export = ctx.obj['EXPORT']
118+
file = ctx.obj["FILE"]
119+
quote = ctx.obj["QUOTE"]
120+
export = ctx.obj["EXPORT"]
103121
success, key, value = set_key(file, key, value, quote, export)
104122
if success:
105-
click.echo(f'{key}={value}')
123+
click.echo(f"{key}={value}")
106124
else:
107125
exit(1)
108126

109127

110128
@cli.command()
111129
@click.pass_context
112-
@click.argument('key', required=True)
130+
@click.argument("key", required=True)
113131
def get(ctx: click.Context, key: Any) -> None:
114132
"""Retrieve the value for the given key."""
115-
file = ctx.obj['FILE']
133+
file = ctx.obj["FILE"]
116134

117135
with stream_file(file) as stream:
118136
values = dotenv_values(stream=stream)
@@ -126,33 +144,32 @@ def get(ctx: click.Context, key: Any) -> None:
126144

127145
@cli.command()
128146
@click.pass_context
129-
@click.argument('key', required=True)
147+
@click.argument("key", required=True)
130148
def unset(ctx: click.Context, key: Any) -> None:
131149
"""Removes the given key."""
132-
file = ctx.obj['FILE']
133-
quote = ctx.obj['QUOTE']
150+
file = ctx.obj["FILE"]
151+
quote = ctx.obj["QUOTE"]
134152
success, key = unset_key(file, key, quote)
135153
if success:
136154
click.echo(f"Successfully removed {key}")
137155
else:
138156
exit(1)
139157

140158

141-
@cli.command(context_settings={'ignore_unknown_options': True})
159+
@cli.command(context_settings={"ignore_unknown_options": True})
142160
@click.pass_context
143161
@click.option(
144162
"--override/--no-override",
145163
default=True,
146164
help="Override variables from the environment file with those from the .env file.",
147165
)
148-
@click.argument('commandline', nargs=-1, type=click.UNPROCESSED)
166+
@click.argument("commandline", nargs=-1, type=click.UNPROCESSED)
149167
def run(ctx: click.Context, override: bool, commandline: List[str]) -> None:
150168
"""Run command with environment variables present."""
151-
file = ctx.obj['FILE']
169+
file = ctx.obj["FILE"]
152170
if not os.path.isfile(file):
153171
raise click.BadParameter(
154-
f'Invalid value for \'-f\' "{file}" does not exist.',
155-
ctx=ctx
172+
f"Invalid value for '-f' \"{file}\" does not exist.", ctx=ctx
156173
)
157174
dotenv_as_dict = {
158175
k: v
@@ -161,7 +178,7 @@ def run(ctx: click.Context, override: bool, commandline: List[str]) -> None:
161178
}
162179

163180
if not commandline:
164-
click.echo('No command given.')
181+
click.echo("No command given.")
165182
exit(1)
166183
run_command(commandline, dotenv_as_dict)
167184

@@ -190,14 +207,10 @@ def run_command(command: List[str], env: Dict[str, str]) -> None:
190207
cmd_env = os.environ.copy()
191208
cmd_env.update(env)
192209

193-
if sys.platform == 'win32':
210+
if sys.platform == "win32":
194211
# execvpe on Windows returns control immediately
195212
# rather than once the command has finished.
196-
p = Popen(command,
197-
universal_newlines=True,
198-
bufsize=0,
199-
shell=False,
200-
env=cmd_env)
213+
p = Popen(command, universal_newlines=True, bufsize=0, shell=False, env=cmd_env)
201214
_, _ = p.communicate()
202215

203216
exit(p.returncode)

0 commit comments

Comments
 (0)