linting¶
buff lints and formats .by source. it is ruff, so it reads ruff's
configuration out of pyproject.toml and every rule ruff ships is available:
BY is basedpython's own rule prefix. most of those rules look for a python
spelling of something basedpython has syntax for:
| code | name | what it finds |
|---|---|---|
BY001 |
manual-none-coalesce |
a conditional expression that is a ?? |
BY002 |
manual-optional-chain |
a conditional expression that is a ?. |
BY003 |
manual-isinstance |
an isinstance call that is an is |
BY004 |
manual-super-call |
super() where super will do |
BY007 |
manual-any-annotation |
Any where dynamic will do |
BY009 |
manual-unpack-annotation |
Unpack[…] where * will do |
BY010 |
manual-typeof-annotation |
TypeOf[…] where typeof will do |
BY011 |
manual-re-export |
import name as name, which is export |
BY012 |
redundant-typing-import |
an import of a member already implicit |
BY017 |
unnecessary-stub-body |
a : ... body an empty declaration needs no more |
BY019 |
manual-sentinel |
a Sentinel(…) assignment, which is sentinel |
BY020 |
manual-cast-call |
a typing.cast call, which is the cast keyword |
BY101 |
redundant-none-coalesce |
a ?? whose fallback cannot change the result |
every one of them is fixable. BY020's fix is the only one that is always
unsafe — basedpython's cast is checked where typing.cast
is a no-op, so the rewrite adds a way for the program to fail.
they also compose with the upstream rules that produce their input. SIM108
turns an if / else block into a conditional expression, and BY001 takes it
the rest of the way, so one --fix run rewrites
into
what the linter does not check¶
the linter reads one file at a time and has no types. anything that takes a type
to decide is by check's job instead, and reporting it in both places would
mean two answers that can disagree. that split is why a once callback the
callee never calls is once-not-called in the checker and has no BY rule
here, and why t[0] is not reported as a
tuple member access — whether t is a tuple is a type
question
upstream rules on .by source¶
ruff's own rules are written for python source, and two things follow from that.
a rule that resolves a name through its import does not see a name basedpython
resolves for you. UP045 rewrites Optional[int] to int | None when
Optional was imported, and says nothing when it was left to
implicit typing — the same annotation, reported or not
depending on a line that basedpython does not need
a rule that suggests a replacement suggests the python one. SIM108 above is
the case where that composes; where it does not, the suggestion is still valid
.by, just not the shortest way to write it
nothing in ruff's rule set is known to report a construct that is correct
basedpython. F821 used to: an unqualified builder inside a
trailing-lambda block resolves against the block's
implicit receiver, and the linter cannot see receiver
types, so it now defers every unresolved name inside a block to by check. the
same deferral covers self and an
enum variant written bare