Skip to content

Commit e98708b

Browse files
authored
Merge branch 'main' into code
2 parents df4c1d2 + 07a4645 commit e98708b

15 files changed

+2101
-115
lines changed

.github/workflows/codespell-private.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
REQUIRE_ASPELL: true
1515
RUFF_OUTPUT_FORMAT: github
1616
# Make sure we're using the latest aspell dictionary
17-
runs-on: ubuntu-22.04
17+
runs-on: ubuntu-latest
1818
timeout-minutes: 10
1919
strategy:
2020
fail-fast: false
@@ -25,6 +25,7 @@ jobs:
2525
- "3.10"
2626
- "3.11"
2727
- "3.12"
28+
- "3.13"
2829
no-toml:
2930
- ""
3031
include:
@@ -39,6 +40,7 @@ jobs:
3940
uses: actions/setup-python@v5
4041
with:
4142
python-version: ${{ matrix.python-version }}
43+
allow-prereleases: true
4244
- run: sudo apt-get install libaspell-dev aspell-en
4345
- name: Install dependencies
4446
run: |
@@ -50,7 +52,7 @@ jobs:
5052
- run: codespell --help
5153
- run: codespell --version
5254
- run: make check
53-
- uses: codecov/codecov-action@v4
55+
- uses: codecov/codecov-action@v5
5456
with:
5557
token: ${{ secrets.CODECOV_TOKEN }}
5658
# tomli should not be required for the next two steps (and make sure it's not)

.github/workflows/codespell-windows.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@ jobs:
2525
- run: codespell --help
2626
- run: codespell --version
2727
- run: pytest codespell_lib
28-
- uses: codecov/codecov-action@v4
28+
- uses: codecov/codecov-action@v5
2929
with:
3030
token: ${{ secrets.CODECOV_TOKEN }}

.github/workflows/release.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ on:
66
types: [published]
77
push:
88
branches:
9-
- master
9+
- main
1010
pull_request:
1111
branches:
12-
- master
12+
- main
1313

1414
permissions:
1515
contents: read

.pre-commit-config.yaml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ exclude: ^(\.[^/]*cache/.*)$
44
repos:
55
- repo: https://github.com/executablebooks/mdformat
66
# Do this before other tools "fixing" the line endings
7-
rev: 0.7.17
7+
rev: 0.7.18
88
hooks:
99
- id: mdformat
1010
name: Format Markdown
@@ -26,10 +26,10 @@ repos:
2626
hooks:
2727
- id: rst-linter
2828
- repo: https://github.com/pre-commit/pre-commit-hooks
29-
rev: v4.6.0
29+
rev: v5.0.0
3030
hooks:
3131
- id: no-commit-to-branch
32-
args: [--branch, master]
32+
args: [--branch, main]
3333
- id: check-yaml
3434
args: [--unsafe]
3535
- id: debug-statements
@@ -58,7 +58,7 @@ repos:
5858
- -d
5959
- "{extends: relaxed, rules: {line-length: {max: 90}}}"
6060
- repo: https://github.com/astral-sh/ruff-pre-commit
61-
rev: v0.4.9
61+
rev: v0.7.3
6262
hooks:
6363
- id: ruff
6464
- id: ruff-format
@@ -75,11 +75,11 @@ repos:
7575
additional_dependencies:
7676
- tomli
7777
- repo: https://github.com/abravalheri/validate-pyproject
78-
rev: v0.18
78+
rev: v0.23
7979
hooks:
8080
- id: validate-pyproject
8181
- repo: https://github.com/pre-commit/mirrors-mypy
82-
rev: v1.10.0
82+
rev: v1.13.0
8383
hooks:
8484
- id: mypy
8585
args: ["--config-file", "pyproject.toml"]

README.rst

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ You can select the optional dictionaries with the ``--builtin`` option.
119119
Ignoring words
120120
--------------
121121

122-
When ignoring false positives, note that spelling errors are *case-insensitive* but words to ignore are *case-sensitive*. For example, the dictionary entry ``wrod`` will also match the typo ``Wrod``, but to ignore it you must pass ``wrod``.
122+
When ignoring false positives, note that spelling errors are *case-insensitive* but words to ignore are *case-sensitive*. For example, the dictionary entry ``wrod`` will also match the typo ``Wrod``, but to ignore it you must pass ``Wrod``.
123123

124124
The words to ignore can be passed in two ways:
125125

@@ -161,7 +161,8 @@ Using a config file
161161

162162
Command line options can also be specified in a config file.
163163

164-
When running ``codespell``, it will check in the current directory for a file
164+
When running ``codespell``, it will check in the current directory for an
165+
`INI file <https://en.wikipedia.org/wiki/INI_file>`_
165166
named ``setup.cfg`` or ``.codespellrc`` (or a file specified via ``--config``),
166167
containing an entry named ``[codespell]``. Each command line argument can
167168
be specified in this file (without the preceding dashes), for example:
@@ -173,15 +174,16 @@ be specified in this file (without the preceding dashes), for example:
173174
count =
174175
quiet-level = 3
175176
176-
The ``.codespellrc`` file is an `INI file <https://en.wikipedia.org/wiki/INI_file>`_,
177-
which is read using Python's
178-
`configparser <https://docs.python.org/3/library/configparser.html#supported-ini-file-structure>`_.
179-
For example, comments are possible using ``;`` or ``#`` as the first character.
177+
Python's
178+
`configparser <https://docs.python.org/3/library/configparser.html#supported-ini-file-structure>`_
179+
module defines the exact format of INI config files. For example,
180+
comments are possible using ``;`` or ``#`` as the first character.
180181

181182
Codespell will also check in the current directory for a ``pyproject.toml``
182-
(or a path can be specified via ``--toml <filename>``) file, and the
183-
``[tool.codespell]`` entry will be used, but only if the tomli_ package
184-
is installed for versions of Python prior to 3.11. For example:
183+
file (or a file specified via ``--toml``), and the ``[tool.codespell]``
184+
entry will be used. For versions of Python prior to 3.11, this requires
185+
the tomli_ package. For example, here is the TOML equivalent of the
186+
previous config file:
185187

186188
.. code-block:: toml
187189
@@ -190,25 +192,40 @@ is installed for versions of Python prior to 3.11. For example:
190192
count = true
191193
quiet-level = 3
192194
193-
These are both equivalent to running:
195+
The above INI and TOML files are equivalent to running:
194196

195197
.. code-block:: sh
196198
197-
codespell --quiet-level 3 --count --skip "*.po,*.ts,./src/3rdParty,./src/Test"
199+
codespell --skip "*.po,*.ts,./src/3rdParty,./src/Test" --count --quiet-level 3
198200
199201
If several config files are present, they are read in the following order:
200202

201-
#. ``pyproject.toml`` (only if the ``tomli`` library is available)
203+
#. ``pyproject.toml`` (only if the ``tomli`` library is available for Python < 3.11)
202204
#. ``setup.cfg``
203205
#. ``.codespellrc``
204206
#. any additional file supplied via ``--config``
205207

206208
If a codespell configuration is supplied in several of these files,
207209
the configuration from the most recently read file overwrites previously
208-
specified configurations.
210+
specified configurations. Any options specified in the command line will
211+
*override* options from the config files.
209212

210-
Any options specified in the command line will *override* options from the
211-
config files.
213+
Values in a config file entry cannot start with a ``-`` character, so if
214+
you need to do this, structure your entries like this:
215+
216+
.. code-block:: ini
217+
218+
[codespell]
219+
dictionary = mydict,-
220+
ignore-words = bar,-foo
221+
222+
instead of these invalid entries:
223+
224+
.. code-block:: ini
225+
226+
[codespell]
227+
dictionary = -,mydict
228+
ignore-words = -foo,bar
212229
213230
.. _tomli: https://pypi.org/project/tomli/
214231

@@ -347,13 +364,13 @@ In the scenario where the user prefers not to follow the development version of
347364

348365
.. code-block:: sh
349366
350-
wget https://raw.githubusercontent.com/codespell-project/codespell/master/codespell_lib/data/dictionary.txt
367+
wget https://raw.githubusercontent.com/codespell-project/codespell/main/codespell_lib/data/dictionary.txt
351368
codespell -D dictionary.txt
352369
353370
The above simply downloads the latest ``dictionary.txt`` file and then by utilizing the ``-D`` flag allows the user to specify the freshly downloaded ``dictionary.txt`` as the custom dictionary instead of the default one.
354371

355372
You can also do the same thing for the other dictionaries listed here:
356-
https://github.com/codespell-project/codespell/tree/master/codespell_lib/data
373+
https://github.com/codespell-project/codespell/tree/main/codespell_lib/data
357374

358375
License
359376
-------

codespell_lib/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
from ._codespell import _script_main, main
22
from ._version import __version__ # type: ignore[import-not-found]
33

4-
__all__ = ["_script_main", "main", "__version__"]
4+
__all__ = ["__version__", "_script_main", "main"]

codespell_lib/__main__.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,4 @@
33
from ._codespell import _script_main
44

55
if __name__ == "__main__":
6-
try:
7-
sys.exit(_script_main())
8-
except KeyboardInterrupt:
9-
pass
6+
sys.exit(_script_main())

codespell_lib/_codespell.py

Lines changed: 73 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import re
2626
import sys
2727
import textwrap
28-
from ctypes import wintypes
2928
from typing import (
3029
Any,
3130
Dict,
@@ -36,9 +35,16 @@
3635
Pattern,
3736
Sequence,
3837
Set,
38+
TextIO,
3939
Tuple,
4040
)
4141

42+
if sys.platform == "win32":
43+
from ctypes import wintypes
44+
45+
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
46+
STD_OUTPUT_HANDLE = wintypes.HANDLE(-11)
47+
4248
from ._spellchecker import Misspelling, build_dict
4349
from ._text_util import fix_case
4450

@@ -137,10 +143,6 @@
137143
EX_DATAERR = 65
138144
EX_CONFIG = 78
139145

140-
# Windows specific constants
141-
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
142-
STD_OUTPUT_HANDLE = wintypes.HANDLE(-11)
143-
144146
# OPTIONS:
145147
#
146148
# ARGUMENTS:
@@ -201,11 +203,17 @@ def __str__(self) -> str:
201203

202204

203205
class FileOpener:
204-
def __init__(self, use_chardet: bool, quiet_level: int) -> None:
206+
def __init__(
207+
self,
208+
use_chardet: bool,
209+
quiet_level: int,
210+
ignore_multiline_regex: Optional[Pattern[str]],
211+
) -> None:
205212
self.use_chardet = use_chardet
206213
if use_chardet:
207214
self.init_chardet()
208215
self.quiet_level = quiet_level
216+
self.ignore_multiline_regex = ignore_multiline_regex
209217

210218
def init_chardet(self) -> None:
211219
try:
@@ -247,7 +255,7 @@ def open_with_chardet(self, filename: str) -> Tuple[List[str], str]:
247255
)
248256
raise
249257
else:
250-
lines = f.readlines()
258+
lines = self.get_lines(f)
251259
f.close()
252260

253261
return lines, f.encoding
@@ -262,7 +270,7 @@ def open_with_internal(self, filename: str) -> Tuple[List[str], str]:
262270
print(f'WARNING: Trying next encoding "{encoding}"', file=sys.stderr)
263271
with open(filename, encoding=encoding, newline="") as f:
264272
try:
265-
lines = f.readlines()
273+
lines = self.get_lines(f)
266274
except UnicodeDecodeError:
267275
if not self.quiet_level & QuietLevels.ENCODING:
268276
print(
@@ -279,6 +287,22 @@ def open_with_internal(self, filename: str) -> Tuple[List[str], str]:
279287

280288
return lines, encoding
281289

290+
def get_lines(self, f: TextIO) -> List[str]:
291+
if self.ignore_multiline_regex:
292+
text = f.read()
293+
pos = 0
294+
text2 = ""
295+
for m in re.finditer(self.ignore_multiline_regex, text):
296+
text2 += text[pos : m.start()]
297+
# Replace with blank lines so line numbers are unchanged.
298+
text2 += "\n" * m.group().count("\n")
299+
pos = m.end()
300+
text2 += text[pos:]
301+
lines = text2.split("\n")
302+
else:
303+
lines = f.readlines()
304+
return lines
305+
282306

283307
# -.-:-.-:-.-:-.:-.-:-.-:-.-:-.-:-.:-.-:-.-:-.-:-.-:-.:-.-:-
284308

@@ -411,6 +435,19 @@ def parse_options(
411435
'e.g., "\\bmatch\\b". Defaults to '
412436
"empty/disabled.",
413437
)
438+
parser.add_argument(
439+
"--ignore-multiline-regex",
440+
action="store",
441+
type=str,
442+
help="regular expression that is used to ignore "
443+
"text that may span multi-line regions. "
444+
"The regex is run with re.DOTALL. For example to "
445+
"allow skipping of regions of Python code using "
446+
"begin/end comments one could use: "
447+
"--ignore-multiline-regex "
448+
"'# codespell:ignore-begin *\\n.*# codespell:ignore-end *\\n'. "
449+
"Defaults to empty/disabled.",
450+
)
414451
parser.add_argument(
415452
"-I",
416453
"--ignore-words",
@@ -1063,7 +1100,12 @@ def flatten_clean_comma_separated_arguments(
10631100

10641101
def _script_main() -> int:
10651102
"""Wrap to main() for setuptools."""
1066-
return main(*sys.argv[1:])
1103+
try:
1104+
return main(*sys.argv[1:])
1105+
except KeyboardInterrupt:
1106+
# User has typed CTRL+C
1107+
sys.stdout.write("\n")
1108+
return 130
10671109

10681110

10691111
def _usage_error(parser: argparse.ArgumentParser, message: str) -> int:
@@ -1090,6 +1132,9 @@ def main(*args: str) -> int:
10901132
for ifile, cfg_file in enumerate(used_cfg_files, start=1):
10911133
print(f" {ifile}: {cfg_file}")
10921134

1135+
if options.interactive > 0:
1136+
options.write_changes = True
1137+
10931138
if options.regex and options.write_changes:
10941139
return _usage_error(
10951140
parser,
@@ -1115,6 +1160,20 @@ def main(*args: str) -> int:
11151160
else:
11161161
ignore_word_regex = None
11171162

1163+
if options.ignore_multiline_regex:
1164+
try:
1165+
ignore_multiline_regex = re.compile(
1166+
options.ignore_multiline_regex, re.DOTALL
1167+
)
1168+
except re.error as e:
1169+
return _usage_error(
1170+
parser,
1171+
f"ERROR: invalid --ignore-multiline-regex "
1172+
f'"{options.ignore_multiline_regex}" ({e})',
1173+
)
1174+
else:
1175+
ignore_multiline_regex = None
1176+
11181177
ignore_words, ignore_words_cased = parse_ignore_words_option(
11191178
options.ignore_words_list
11201179
)
@@ -1203,7 +1262,11 @@ def main(*args: str) -> int:
12031262
for exclude_file in exclude_files:
12041263
build_exclude_hashes(exclude_file, exclude_lines)
12051264

1206-
file_opener = FileOpener(options.hard_encoding_detection, options.quiet_level)
1265+
file_opener = FileOpener(
1266+
options.hard_encoding_detection,
1267+
options.quiet_level,
1268+
ignore_multiline_regex,
1269+
)
12071270

12081271
glob_match = GlobMatch(
12091272
flatten_clean_comma_separated_arguments(options.skip) if options.skip else []

0 commit comments

Comments
 (0)