<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:
- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
requests.)
- Does this pull request include references to any relevant issues?
-->
## Summary
This PR is related to https://github.com/apache/airflow/issues/48389.
In Airflow 3, the function signature for `airflow.Dataset`,
`airflow.datasets.Dataset`, and `airflow.sdk.Asset` has changed. Below
is the function signature for `airflow.sdk.Asset`. The second positional
argument is `uri` which is of type `str`. In the older version, the
second positional argument can be a dictionary which is the `extra:
'dict | None' = None` argument. Therefore, we need to flag this in
Airflow 3.
```python
Asset(name: 'str | None' = None, uri: 'str | None' = None, *, group: 'str | None' = None, extra: 'dict | None' = None, watchers: 'list[AssetWatcher | SerializedAssetWatcher] | None' = None) -> 'None'
```
As this is a check on constructor call, we need to create a new method
`check_constructor_arguments`, instead of on method call. The new rule
check whether the index 1 (the second argument) is a dictionary (either
a literal or a `dict()` call).
## Test Plan
The `AIR303.py` test file have been updated with new test cases. The
snapshot has been updated and all other test cases passed.
@Lee-W , could you please review it when you have a chance, and let me
know if you have further feedback. Thanks!
## Summary
https://github.com/astral-sh/ty/issues/111
this pr adds an `invalid-dataclass-override` diagnostic when a custom
`__setattr__` or `__delattr__` is defined on a dataclass where
`frozen=True`
([docs](https://docs.python.org/3/library/dataclasses.html#frozen-instances))
### Runtime exception
```
Traceback (most recent call last):
File "/Users/justinchapman/src/ty-playground/main.py", line 4, in <module>
@dataclass(frozen=True)
~~~~~~~~~^^^^^^^^^^^^^
File "/Users/justinchapman/.local/share/uv/python/cpython-3.13.0-macos-aarch64-none/lib/python3.13/dataclasses.py", line 1295, in wrap
return _process_class(cls, init, repr, eq, order, unsafe_hash,
frozen, match_args, kw_only, slots,
weakref_slot)
File "/Users/justinchapman/.local/share/uv/python/cpython-3.13.0-macos-aarch64-none/lib/python3.13/dataclasses.py", line 1157, in _process_class
func_builder.add_fns_to_class(cls)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^
File "/Users/justinchapman/.local/share/uv/python/cpython-3.13.0-macos-aarch64-none/lib/python3.13/dataclasses.py", line 516, in add_fns_to_class
raise TypeError(error_msg)
TypeError: Cannot overwrite attribute __setattr__ in class A
```
### Diagnostic
```
error[invalid-dataclass-override]: Cannot overwrite attribute __setattr__ in class A
--> /Users/justinchapman/src/ty-playground/main.py:6:5
|
4 | @dataclass(frozen=True)
5 | class A:
6 | def __setattr__(self, name: str, value: object) -> None: ...
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
info: __setattr__
info: rule `invalid-dataclass-override` is enabled by default
Found 1 diagnostic
```
## Test Plan
- new mdtests
- e2e
- the `attrs` mypy primer diff looks to be a [true
positive](https://github.com/python-attrs/attrs/blob/main/tests/test_setattr.py#L373)
- the other results have been unpredictable and have changed every time
i pushed new code, even if the diagnostic logic didn't change...
---------
Co-authored-by: Carl Meyer <carl@astral.sh>
Hello,
This MR adds a new rule and its fix, `RUF069`,
`DuplicateEntryInDunderAll`. I'm using `RUF069` because we already have
[RUF068](https://github.com/astral-sh/ruff/pull/20585) and
[RUF069](https://github.com/astral-sh/ruff/pull/21079#issuecomment-3493839453)
in the works.
The rule job is to prevent users from accidentally adding duplicate
entries to `__all__`, which, for example, can result from copy-paste
mistakes.
It deals with the following syntaxes:
```python
__all__: list[str] = ["a", "a"]
__all__: typing.Any = ("a", "a")
__all__.extend(["a", "a"])
__all__ += ["a", "a"]
```
But it does not keep track of `__all__` contents, meaning the following
code snippet is a false negative:
```python
class A: ...
__all__ = ["A"]
__all__.extend(["A"])
```
## Violation Example
```console
RUF069 `__all__` contains duplicate entries
--> RUF069.py:2:17
|
1 | __all__ = ["A", "A", "B"]
| ^^^
help: Remove duplicate entries from `__all__`
1 | __all__ = ["A", "B"]
- __all__ = ["A", "A", "B"]
```
## Ecosystem Report
The `ruff-ecosystem` results contain seven violations in four projects,
all of them seem like true positives, with one instance appearing to be
an actual bug.
This [code
snippet](90d855985b/stubs/reportlab/reportlab/lib/rltempfile.pyi (L4))
from `reportlab` contains the same entry twice instead of exporting both
functions.
```python
def get_rl_tempdir(*subdirs: str) -> str: ...
def get_rl_tempfile(fn: str | None = None) -> str: ...
__all__ = ("get_rl_tempdir", "get_rl_tempdir")
```
Closes [#21945](https://github.com/astral-sh/ruff/issues/21945)
---------
Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
# Summary
- Report a single combined UnusedNOQA diagnostic when any range
suppression
has one or more unused, disabled, or duplicate lint codes.
- Report a single combined InvalidRuleCode diagnostic for any range
suppression
with one or more invalid lint codes.
# Test Plan
Updated fixtures and snapshots
Fixes#22429, #21873
## Summary
Currently we don't think that namespace packages (e.g. `google` after
you've pip-installed `google-cloud-ndb`) have attributes such as
`__file__`, `__name__`, etc. This PR fixes that.
## Test Plan
Mdtests and snapshots.
- Adds `Tokens::split_at()` to get tokens before/after an offset.
- Updates `Suppressions::load_from_tokens` to take an `Indexer` and use
comment ranges to minimize the need for walking tokens looking for
indent/dedent.
Adapted from
https://github.com/astral-sh/ruff/pull/21441#pullrequestreview-3503773083Fixes#22087
The implementation here is (to me) surprisingly complicated. The main
complications are:
1. Trying to limit the redundant detection to as few of the symbols
we extract as possible. In particular, while I haven't done benchmarking
on this, I perceive the redundancy detection to be somewhat expensive
and auto-import can return lots of symbols. So we're careful to only do
this extra checking on (typically) small groups of symbols that could
possibly be merged.
2. Even by restricting our work, this merging process could still be
called quite a bit. (Thousands of times in my "standard data scienc-y
test environment.") So I went out of my way to amortize allocs.
3. Re-exports can form a chain and we want to find all of them.
4. We (probably) don't want to remove redundant re-exports unless
they share the same top-level module. Otherwise, e.g., a library
that re-exports another library's symbols could have all of its
re-exports dropped.
5. We want to only keep the top-most re-exports, and there may be
multiple such re-exports. So we keep all of them.
6. We can't assume anything about the relationship of re-exports
and the original definition. A re-export could be deeper in the
module hierarchy than the original definition or above it.
The invariants established by the constructors for `ModuleName`
guarantee that this is always available. It's useful for
determining the "top level" module for where a symbol lives.
Previously, we were constructing this at a higher level layer. But this
commit pushes it down a layer of abstraction. This shouldn't result in
constructing the fully qualified name any more frequently than we
previously did. Namely, we're still only doing it for symbols that match
the caller provided search query.
The motivation here is that we want to do some de-duplication based on
module name, and having the fully qualified name of a symbol seems like
the most straight-forward way to go about this. (We'll need one more
piece of information that we add in a subsequent commit.)
Summary
--
The WASM builds in the 0.14.12 release were failing with an error about
trying
to publish over an existing version 5.8.1 ([example]). This seems to be
because
it's trying to publish a package named `pkg` instead of the `pkg`
directory.
Micha found that a trailing slash causes `npm` to interpret the path
correctly.
Test Plan
--
I think we can test this with a dry run of the release workflow,
otherwise
another release attempt for 0.14.12
[example]:
https://github.com/astral-sh/ruff/actions/runs/21037103249/job/60492735499#step:5:281