Default and overriding patterns¶
A walker can judge files and directories by patterns that your code adds, as well as or instead of ignore-files. You write each pattern exactly like a line of an ignore-file, and its weight decides whether it's one of your program's defaults, which the ignore-files can overrule, or it overrides them.
For example, this walker skips every file whose name ends with .pdf, unless an ignore-file re-includes it, and every directory named secrets, whatever the ignore-files say:
from mosey import Mosey
builder = Mosey()
builder.set_ignore_filename(".walkignore")
builder.add_pattern("*.pdf")
builder.add_pattern("secrets/", weight=1)
walker = builder.build()
Rules¶
Writing patterns¶
- A pattern means exactly what the same line would mean in an ignore-file in the directory you walk, following the ignore-files' rules for writing patterns and matching names and paths.
- A
/at the start or in the middle ties a pattern to the directory you walk, so/todo.txtonly matches thetodo.txtin that directory. - Patterns apply even when the walker reads no ignore-files.
Weights¶
Mosey.add_patterngives a pattern a weight of 0, unless you give it another weight.- A pattern weighing 1 or more overrules every ignore-file.
- An ignore-file overrules the patterns weighing 0 or less, for everything inside its directory.
- Between patterns, the heavier wins, and at equal weights, the last one added wins.
- Mosey never walks into an ignored directory, so nothing inside it can be re-included, not even by a pattern weighing 1 or more.
Refused patterns¶
Mosey.add_patternraisesValueErrorfor a pattern that an ignore-file would read differently, that's broken, or that holds a backslash that escapes nothing.
| Pattern | Why | Write instead |
|---|---|---|
#notes |
An ignore-file would read it as a comment. | \#notes |
build\out |
A backslash escapes nothing before a character that a glob doesn't use. | build/out |
./todo.txt |
No path has a . or .. segment. |
/todo.txt |
docs//todo.txt |
No path has an empty segment. | docs/todo.txt |
[abc.txt |
Nothing closes the [. |
\[abc.txt |
[[:letter:]] |
[:letter:] isn't a class. |
[[:alpha:]] |
notes\ |
A backslash at the end escapes nothing. | notes/ |
!, / or an empty pattern |
There's nothing to match. | |
| A line break | No line of an ignore-file can hold one. | |
| A null character | No name can hold one. | |
| A carriage return at the end | An ignore-file removes one from the end of each line. | [[:cntrl:]] in its place |
| A byte order mark at the start | An ignore-file removes one from its start. |
Example¶
Microsoft Windows
Windows conventionally separates path segments with backslashes, like docs\guide.pdf.
Patterns always separate path segments with forward slashes, on every operating system, and a backslash in a pattern is always an escape. On Windows, os.path.join and str(Path(...)) build paths with backslashes, so join a pattern's segments with forward slashes, or build it with PurePath.as_posix().
Mosey refuses a pattern with a backslash that escapes nothing, which catches most Windows paths, like docs\guide.pdf and node_modules\@types. A backslash only escapes \, *, ?, [, ], !, #, a space, - or ^.
The paths in this example are each file's Step.relative_as_posix — its path relative to the walked directory, with forward slashes.
Windows usually ignores case in filenames, but patterns don't, so *.pdf doesn't match REPORT.PDF. Write *.[Pp][Dd][Ff] to match both.
These conventions aside, Mosey supports Windows as a first-class platform. Every code change is tested on Linux, macOS, and Windows, for compatibility and feature parity.
Take a directory with these files, subdirectories and ignore-files:
root/
├── .walkignore
├── backup.iso
├── docs/
│ ├── .walkignore
│ ├── guide.pdf
│ └── install.iso
├── manual.pdf
├── readme.md
└── report.pdf
The .walkignore ignore-files hold these lines:
A walker that reads those ignore-files, and adds two patterns:
builder = Mosey()
builder.set_ignore_filename(".walkignore")
builder.add_pattern("*.pdf")
builder.add_pattern("*.iso", weight=1)
walker = builder.build()
...yields these files when it walks the root directory, in the usual walk order:
Why?
| Path | Result | Why |
|---|---|---|
.walkignore |
Yielded | Nothing matches it. |
backup.iso |
Ignored | *.iso matches it. |
docs/ |
Walked | Nothing matches it. |
docs/.walkignore |
Yielded | Nothing matches it. |
docs/guide.pdf |
Ignored | *.pdf matches it. |
docs/install.iso |
Ignored | docs/.walkignore's !*.iso matches it, but *.iso weighs 1, so it overrules every ignore-file. |
manual.pdf |
Yielded | *.pdf matches it, but it weighs 0, so the root's !manual.pdf overrules it. |
readme.md |
Yielded | Nothing matches it. |
report.pdf |
Ignored | *.pdf matches it. |
Behaviour you might not expect¶
- An ignore-file can undo a pattern weighing 0 or less. If you add
secrets/with its usual weight of 0, an ignore-file holding!secrets/re-includes those directories. If no ignore-file should undo a pattern, give it a weight of 1. - A pattern is never an absolute path.
/home/me/buildmeanshome/me/buildinside the directory you walk, not the directory at that path. Give a pattern relative to the directory you walk. - Patterns are tied to whichever directory you walk. A walker that walks two directories ties
/todo.txtto each in turn, so it skips thetodo.txtin each one. - Patterns never judge the directory you walk. Walking a directory named
buildwith the patternbuild/still walks it, and only skips thebuilddirectories inside it. - A broken line in an ignore-file matches nothing, but adding the same pattern raises. An ignore-file's
[abc.txtis just skipped, whileMosey.add_patternraisesValueErrorfor it.