0
# doc8
1
2
An opinionated style checker for reStructuredText (RST) documentation that helps developers maintain consistent formatting and style in their documentation. doc8 validates RST format, enforces line length limits with intelligent exceptions for URLs and literal blocks, prevents trailing whitespace and tab indentation, ensures Unix newlines, and checks for proper file endings.
3
4
## Package Information
5
6
- **Package Name**: doc8
7
- **Package Type**: pypi
8
- **Language**: Python
9
- **Installation**: `pip install doc8`
10
11
## Core Imports
12
13
```python
14
import doc8
15
```
16
17
For programmatic use:
18
19
```python
20
from doc8 import doc8
21
```
22
23
For accessing version information:
24
25
```python
26
from doc8 import __version__
27
```
28
29
## Basic Usage
30
31
### Command Line Usage
32
33
```bash
34
# Check all documentation files in current directory
35
doc8
36
37
# Check specific directory
38
doc8 my-project/docs
39
40
# Check with specific options
41
doc8 --max-line-length=99 --ignore=D001 docs/
42
43
# Check with configuration file
44
doc8 --config=my-doc8.ini docs/
45
```
46
47
### Programmatic Usage
48
49
```python
50
from doc8 import doc8
51
52
# Basic validation with default settings
53
result = doc8()
54
55
# Custom validation with options
56
result = doc8(
57
paths=['docs/'],
58
max_line_length=99,
59
ignore=['D001'],
60
allow_long_titles=True
61
)
62
63
# Check results
64
print(f"Total errors: {result.total_errors}")
65
print(f"Files checked: {result.files_selected}")
66
print(f"Files ignored: {result.files_ignored}")
67
68
# Get detailed report
69
print(result.report())
70
71
# Access individual errors
72
for check_name, filename, line_num, code, message in result.errors:
73
print(f"{filename}:{line_num}: {code} {message}")
74
```
75
76
## Architecture
77
78
doc8 follows a modular check-based architecture:
79
80
- **Main API**: Core doc8() function and CLI entry point for validation orchestration
81
- **Parser**: File parsing with RST document model creation using docutils
82
- **Checks**: Pluggable validation checks implementing LineCheck or ContentCheck interfaces
83
- **Utils**: File discovery and RST processing utilities
84
- **Configuration**: INI/TOML configuration file support with CLI override capabilities
85
- **Extensions**: Plugin system via stevedore for custom validation checks
86
87
## Capabilities
88
89
### Main Validation API
90
91
Primary interface for doc8 validation functionality, supporting both programmatic and command-line usage with extensive configuration options.
92
93
```python { .api }
94
def doc8(args=None, **kwargs):
95
"""
96
Execute doc8 validation on documentation files.
97
98
Args:
99
args (dict, optional): Configuration arguments dictionary
100
**kwargs: Configuration options as keyword arguments
101
102
Returns:
103
Result: Validation results with error details and statistics
104
"""
105
```
106
107
```python { .api }
108
def main():
109
"""
110
Command-line interface entry point for doc8.
111
112
Returns:
113
int: Exit code (0 for success, 1 for errors)
114
"""
115
```
116
117
### Configuration Management
118
119
Functions for loading and managing doc8 configuration from files and command-line arguments.
120
121
```python { .api }
122
def get_defaults():
123
"""
124
Get default configuration settings.
125
126
Returns:
127
dict: Default configuration dictionary
128
"""
129
```
130
131
```python { .api }
132
def extract_config(args):
133
"""
134
Extract configuration from INI or TOML files.
135
136
Args:
137
args (dict): Command-line arguments containing config file paths
138
139
Returns:
140
dict: Configuration dictionary parsed from files
141
"""
142
```
143
144
```python { .api }
145
def from_ini(fp):
146
"""
147
Parse doc8 configuration from INI file.
148
149
Args:
150
fp (str): File path to INI configuration file
151
152
Returns:
153
dict: Configuration dictionary
154
"""
155
```
156
157
```python { .api }
158
def from_toml(fp):
159
"""
160
Parse doc8 configuration from TOML file.
161
162
Args:
163
fp (str): File path to TOML configuration file
164
165
Returns:
166
dict: Configuration dictionary
167
"""
168
```
169
170
### File Processing
171
172
Functions for discovering, scanning, and parsing documentation files for validation.
173
174
```python { .api }
175
def scan(cfg):
176
"""
177
Scan directories for documentation files to validate.
178
179
Args:
180
cfg (dict): Configuration dictionary with paths, extensions, and ignore patterns
181
182
Returns:
183
tuple: (list of ParsedFile objects, count of ignored files)
184
"""
185
```
186
187
```python { .api }
188
def validate(cfg, files, result=None):
189
"""
190
Run validation checks on parsed documentation files.
191
192
Args:
193
cfg (dict): Configuration dictionary
194
files (list): List of ParsedFile objects to validate
195
result (Result, optional): Result object to populate with errors
196
197
Returns:
198
dict: Error counts by check name
199
"""
200
```
201
202
```python { .api }
203
def fetch_checks(cfg):
204
"""
205
Load built-in and extension validation checks.
206
207
Args:
208
cfg (dict): Configuration dictionary
209
210
Returns:
211
list: List of check objects implementing validation interfaces
212
"""
213
```
214
215
### File Parsing
216
217
Classes and functions for parsing documentation files and creating structured representations.
218
219
```python { .api }
220
def parse(filename, encoding=None, default_extension=""):
221
"""
222
Parse a documentation file into a structured representation.
223
224
Args:
225
filename (str): Path to file to parse
226
encoding (str, optional): Text encoding for file reading
227
default_extension (str, optional): Extension to use if file has none
228
229
Returns:
230
ParsedFile: Parsed file object with content and metadata
231
"""
232
```
233
234
```python { .api }
235
class ParsedFile:
236
"""
237
Parsed documentation file with RST processing capabilities.
238
239
Attributes:
240
FALLBACK_ENCODING (str): Default encoding when none specified ("utf-8")
241
"""
242
243
def __init__(self, filename, encoding=None, default_extension=""):
244
"""
245
Initialize parsed file.
246
247
Args:
248
filename (str): Path to the file
249
encoding (str, optional): Text encoding
250
default_extension (str, optional): Default extension if none present
251
"""
252
253
@property
254
def errors(self):
255
"""
256
RST parsing errors from restructuredtext_lint.
257
258
Returns:
259
list: List of parsing error objects
260
"""
261
262
@property
263
def document(self):
264
"""
265
Docutils document object for RST content.
266
267
Returns:
268
docutils.nodes.document: Parsed RST document
269
"""
270
271
@property
272
def lines(self):
273
"""
274
Raw byte lines from file.
275
276
Returns:
277
list: List of byte strings representing file lines
278
"""
279
280
@property
281
def extension(self):
282
"""
283
File extension.
284
285
Returns:
286
str: File extension including dot (e.g., ".rst")
287
"""
288
289
@property
290
def filename(self):
291
"""
292
File path.
293
294
Returns:
295
str: Absolute path to the file
296
"""
297
298
@property
299
def encoding(self):
300
"""
301
Text encoding used for file.
302
303
Returns:
304
str: Character encoding name
305
"""
306
307
@property
308
def raw_contents(self):
309
"""
310
Raw file contents as bytes.
311
312
Returns:
313
bytes: File contents
314
"""
315
316
@property
317
def contents(self):
318
"""
319
Decoded file contents as string.
320
321
Returns:
322
str: File contents as text
323
"""
324
325
def lines_iter(self, remove_trailing_newline=True):
326
"""
327
Iterate over decoded text lines.
328
329
Args:
330
remove_trailing_newline (bool): Whether to strip trailing newlines
331
332
Yields:
333
str: Text lines from file
334
"""
335
```
336
337
### Validation Checks
338
339
Base classes and concrete implementations for documentation validation checks.
340
341
```python { .api }
342
class ContentCheck:
343
"""
344
Abstract base class for checks that operate on entire file content.
345
"""
346
347
def __init__(self, cfg):
348
"""
349
Initialize check with configuration.
350
351
Args:
352
cfg (dict): Configuration dictionary
353
"""
354
355
def report_iter(self, parsed_file):
356
"""
357
Generate validation results for file content.
358
359
Args:
360
parsed_file (ParsedFile): File to validate
361
362
Yields:
363
tuple: (line_number, error_code, message) for each error found
364
"""
365
```
366
367
```python { .api }
368
class LineCheck:
369
"""
370
Abstract base class for checks that operate line-by-line.
371
"""
372
373
def __init__(self, cfg):
374
"""
375
Initialize check with configuration.
376
377
Args:
378
cfg (dict): Configuration dictionary
379
"""
380
381
def report_iter(self, line):
382
"""
383
Generate validation results for a single line.
384
385
Args:
386
line (str): Text line to validate
387
388
Yields:
389
tuple: (error_code, message) for each error found
390
"""
391
```
392
393
```python { .api }
394
class CheckTrailingWhitespace(LineCheck):
395
"""
396
Check for trailing whitespace in lines.
397
398
Attributes:
399
REPORTS (frozenset): Error codes reported (["D002"])
400
"""
401
```
402
403
```python { .api }
404
class CheckIndentationNoTab(LineCheck):
405
"""
406
Check for tab characters in indentation.
407
408
Attributes:
409
REPORTS (frozenset): Error codes reported (["D003"])
410
"""
411
```
412
413
```python { .api }
414
class CheckCarriageReturn(ContentCheck):
415
"""
416
Check for carriage return characters in file content.
417
418
Attributes:
419
REPORTS (frozenset): Error codes reported (["D004"])
420
"""
421
```
422
423
```python { .api }
424
class CheckNewlineEndOfFile(ContentCheck):
425
"""
426
Check that file ends with a newline character.
427
428
Attributes:
429
REPORTS (frozenset): Error codes reported (["D005"])
430
"""
431
```
432
433
```python { .api }
434
class CheckValidity(ContentCheck):
435
"""
436
Check RST syntax validity using docutils.
437
438
Attributes:
439
REPORTS (frozenset): Error codes reported (["D000"])
440
EXT_MATCHER (re.Pattern): Regex matching .rst files
441
WARN_LEVELS (frozenset): Docutils warning levels to report
442
SPHINX_IGNORES_REGEX (list): Patterns to ignore in Sphinx mode
443
"""
444
```
445
446
```python { .api }
447
class CheckMaxLineLength(ContentCheck):
448
"""
449
Check line length limits with RST-aware exceptions.
450
451
Attributes:
452
REPORTS (frozenset): Error codes reported (["D001"])
453
"""
454
```
455
456
### Results and Reporting
457
458
Classes for handling validation results and generating reports.
459
460
```python { .api }
461
class Result:
462
"""
463
Container for doc8 validation results and statistics.
464
"""
465
466
def __init__(self):
467
"""Initialize empty result."""
468
469
@property
470
def total_errors(self):
471
"""
472
Total number of errors found.
473
474
Returns:
475
int: Count of all errors
476
"""
477
478
def error(self, check_name, filename, line_num, code, message):
479
"""
480
Record a validation error.
481
482
Args:
483
check_name (str): Name of check that found error
484
filename (str): File path where error occurred
485
line_num (int): Line number of error
486
code (str): Error code (D000-D005)
487
message (str): Human-readable error description
488
"""
489
490
def finish(self, files_selected, files_ignored, error_counts):
491
"""
492
Finalize results with file and error statistics.
493
494
Args:
495
files_selected (int): Number of files processed
496
files_ignored (int): Number of files ignored
497
error_counts (dict): Error counts by check name
498
"""
499
500
def report(self):
501
"""
502
Generate human-readable validation report.
503
504
Returns:
505
str: Formatted report with error details and statistics
506
"""
507
```
508
509
### Utility Functions
510
511
Helper functions for file discovery, RST processing, and configuration parsing.
512
513
```python { .api }
514
def find_files(paths, extensions, ignored_paths):
515
"""
516
Recursively find documentation files matching extensions.
517
518
Args:
519
paths (list): Root paths to search
520
extensions (set): File extensions to include
521
ignored_paths (list): Paths to ignore (supports globs)
522
523
Yields:
524
tuple: (filepath, is_ignorable) for each matching file
525
"""
526
```
527
528
```python { .api }
529
def filtered_traverse(document, filter_func):
530
"""
531
Traverse docutils document tree with filtering.
532
533
Args:
534
document (docutils.nodes.document): RST document to traverse
535
filter_func (callable): Function to filter nodes
536
537
Yields:
538
docutils.nodes.Node: Filtered document nodes
539
"""
540
```
541
542
```python { .api }
543
def contains_url(line):
544
"""
545
Check if text line contains HTTP or HTTPS URLs.
546
547
Args:
548
line (str): Text line to check
549
550
Returns:
551
bool: True if line contains URLs
552
"""
553
```
554
555
```python { .api }
556
def has_any_node_type(node, node_types):
557
"""
558
Check if node or its ancestors match given types.
559
560
Args:
561
node (docutils.nodes.Node): Node to check
562
node_types (tuple): Node types to match against
563
564
Returns:
565
bool: True if node or ancestor matches types
566
"""
567
```
568
569
```python { .api }
570
def split_set_type(text, delimiter=","):
571
"""
572
Parse delimited text into set of values.
573
574
Args:
575
text (str): Delimited string to parse
576
delimiter (str): Delimiter character
577
578
Returns:
579
set: Set of parsed string values
580
"""
581
```
582
583
```python { .api }
584
def merge_sets(sets):
585
"""
586
Merge multiple sets into single set.
587
588
Args:
589
sets (iterable): Sets to merge
590
591
Returns:
592
set: Combined set containing all values
593
"""
594
```
595
596
```python { .api }
597
def parse_ignore_path_errors(entries):
598
"""
599
Parse path-specific error ignore patterns.
600
601
Args:
602
entries (list): List of "path;error_code;error_code" entries
603
604
Returns:
605
dict: Mapping of paths to sets of ignored error codes
606
"""
607
```
608
609
```python { .api }
610
def setup_logging(verbose):
611
"""
612
Configure logging output based on verbosity setting.
613
614
Args:
615
verbose (bool): Enable verbose logging output
616
"""
617
```
618
619
## Types
620
621
### Configuration Options
622
623
```python { .api }
624
# Configuration dictionary keys and their types
625
ConfigDict = {
626
"paths": list, # Paths to scan for files
627
"config": list, # Configuration file paths
628
"allow_long_titles": bool, # Allow long section titles
629
"ignore": set, # Error codes to ignore
630
"sphinx": bool, # Enable Sphinx-specific ignores
631
"ignore_path": list, # Paths to ignore (globs supported)
632
"ignore_path_errors": dict, # Path-specific error ignores
633
"default_extension": str, # Default file extension
634
"file_encoding": str, # Text encoding for files
635
"max_line_length": int, # Maximum line length
636
"extension": list, # File extensions to check
637
"quiet": bool, # Suppress non-error output
638
"verbose": bool, # Enable verbose output
639
"version": bool, # Show version and exit
640
}
641
```
642
643
### Error Codes
644
645
```python { .api }
646
# Validation error codes
647
ERROR_CODES = {
648
"D000": "Invalid RST format",
649
"D001": "Line too long",
650
"D002": "Trailing whitespace",
651
"D003": "Tab indentation",
652
"D004": "Carriage returns (non-Unix newlines)",
653
"D005": "No newline at end of file",
654
}
655
```
656
657
### Module Constants
658
659
```python { .api }
660
# Default file extensions to check
661
FILE_PATTERNS = [".rst", ".txt"]
662
663
# Default maximum line length
664
MAX_LINE_LENGTH = 79
665
666
# Configuration file search order
667
CONFIG_FILENAMES = [
668
"doc8.ini",
669
".config/doc8.ini",
670
"tox.ini",
671
"pep8.ini",
672
"setup.cfg",
673
"pyproject.toml",
674
]
675
```
676
677
## Configuration
678
679
### Configuration Files
680
681
doc8 automatically searches for configuration in these files:
682
683
- `doc8.ini`
684
- `.config/doc8.ini`
685
- `tox.ini`
686
- `pep8.ini`
687
- `setup.cfg`
688
- `pyproject.toml`
689
690
### INI Format Example
691
692
```ini
693
[doc8]
694
max-line-length = 99
695
ignore = D001,D002
696
ignore-path = build/,temp/
697
ignore-path-errors = docs/legacy.rst;D001;D002
698
allow-long-titles = true
699
verbose = false
700
sphinx = true
701
file-encoding = utf-8
702
default-extension = .rst
703
extensions = .rst,.txt
704
```
705
706
### TOML Format Example
707
708
```toml
709
[tool.doc8]
710
max-line-length = 99
711
ignore = ["D001", "D002"]
712
ignore-path = ["build/", "temp/"]
713
ignore-path-errors = ["docs/legacy.rst;D001;D002"]
714
allow-long-titles = true
715
verbose = false
716
sphinx = true
717
file-encoding = "utf-8"
718
default-extension = ".rst"
719
extensions = [".rst", ".txt"]
720
```
721
722
## Extension System
723
724
doc8 supports custom validation checks through the stevedore plugin system:
725
726
### Plugin Namespace
727
728
- **Entry Point**: `doc8.extension.check`
729
- **Interface**: Checks must inherit from `ContentCheck` or `LineCheck`
730
- **Loading**: Extensions are automatically discovered and loaded
731
732
### Custom Check Example
733
734
```python
735
from doc8.checks import LineCheck
736
737
class MyCustomCheck(LineCheck):
738
REPORTS = frozenset(["C001"])
739
740
def __init__(self, cfg):
741
super().__init__(cfg)
742
743
def report_iter(self, line):
744
if "bad_pattern" in line:
745
yield ("C001", "Found bad pattern")
746
747
# Register in setup.py or pyproject.toml:
748
# [project.entry-points."doc8.extension.check"]
749
# my_check = "mypackage.checks:MyCustomCheck"
750
```