Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,27 @@

## Unreleased

- A changed method is found when the code never names its class: the receiver of a method
call is typed from the pinned release's own annotations, read statically with griffe and
cached per release (a new `receivers` folder in the cache). `df = pd.read_csv(...)` is a
`DataFrame`, `df.groupby(...).count()` a `DataFrameGroupBy.count`, and a class counts with
its base classes across the pinned packages, so `llm.predict(q)` on a `ChatOpenAI` is
langchain-core's `BaseChatModel.predict` in a file that never imports langchain-core. With
pandas 3.0.6, `df.applymap(str)`, `df.swapaxes(0, 1)` and `df.groupby("key", axis=0)` on a
frame from `pd.read_csv` went unflagged; a scratch project pinning pandas 3.0.6 and polars
2.0.0 went from 4 APIs used to 10. A method a class defines itself is its own, not its
base's: `DataFrame.sum` still takes positional arguments where `NDFrame.sum` no longer does.
- Where the receiver cannot be typed, a method whose name is exactly one changed API's across
the packages the code uses, and neither a common Python name nor a member of a built-in or
standard-library type, can count by its name alone. Such name matches are hidden from every
report, the notes, `--fail-on` and `--annotate` unless `--include-name-matches` (`scan`,
`sync`) or `include_name_matches` (MCP) asks for them; they are then tagged `[name match]`.
- A class that became a function, or the reverse, is no longer "old form" at a call site: with
pandas 3.0.6, `with pd.option_context("mode.copy_on_write", True):` was labelled old form
although `option_context` is called the same way as a class and as a function. The call is
"uses this API", and old form only when it passes a keyword the new signature does not take;
a subclass or an `isinstance()` of it stays old form. The note still says to check the new
signature.
- `--base-url` wins over `OPENAI_BASE_URL` for `openai:<model>`, as a flag wins over the
environment for every other setting (#94). The variable overrode the flag; the order is now
the flag, then `OPENAI_BASE_URL`, then `https://api.openai.com/v1`.
Expand Down
17 changes: 12 additions & 5 deletions README.es.md
Original file line number Diff line number Diff line change
Expand Up @@ -828,12 +828,19 @@ reservadas; `verified` se mantiene y significa lo mismo que `checks.example_type
`__getattr__`, pero `run` no los sondea.
- El diff cubre la API pública: se omiten los nombres `_private`, así como los conjuntos de pruebas,
los benchmarks y los ejemplos que se distribuyen dentro de un paquete.
- «Your code uses» (tu código usa) es una coincidencia estática de nombres, archivo por archivo:
- «Your code uses» (tu código usa) es una coincidencia estática, archivo por archivo:
importaciones (incluidos los nombres reexportados), llamadas, lecturas de atributos y argumentos
por nombre. No sigue accesos dinámicos como `getattr`, y por ahora indica archivos, no líneas
([#8](https://github.com/MohammadHijjawi97/since-cutoff/issues/8)). Un parámetro que pasó a ser
obligatorio, solo por nombre o solo posicional aparece siempre como «uses this API», nunca como
«old form», por ahora.
por nombre; el receptor de una llamada a un método se tipa a partir de las anotaciones del propio
paquete (`df = pd.read_csv(...)` es un `DataFrame`; una clase cuenta con sus clases base, a
través de tus paquetes fijados). No sigue accesos dinámicos como `getattr`, y por ahora indica
archivos, no líneas ([#8](https://github.com/MohammadHijjawi97/since-cutoff/issues/8)). Un
método leído sobre un valor cuya clase el código no muestra puede contar solo por su nombre,
cuando ese nombre pertenece a una única API con cambios y no es un nombre común ni un miembro
de un tipo integrado o de la biblioteca estándar: esa coincidencia `[name match]` no aparece
en ningún informe, ni en las notas, ni en `--fail-on` ni en `--annotate` salvo que se indique
`--include-name-matches` (`include_name_matches` por MCP), porque la mayoría son un método
homónimo de otra biblioteca. Un parámetro que pasó a ser obligatorio, solo por nombre o
solo posicional aparece siempre como «uses this API», nunca como «old form», por ahora.
- Una nota solo nombra un sustituto cuando el propio texto de obsolescencia de la biblioteca lo
indica. Los consejos que solo están en una guía de migración (el
[MIGRATION.md](https://github.com/anthropics/anthropic-sdk-python/blob/main/MIGRATION.md) de
Expand Down
20 changes: 14 additions & 6 deletions README.fr.md
Original file line number Diff line number Diff line change
Expand Up @@ -842,12 +842,20 @@ mesuré le test sur les tâches réservées ; `verified` est conservé et signi
`__getattr__`, mais `run` ne les sonde pas.
- Le diff couvre l'API publique : les noms `_private`, ainsi que les suites de tests, benchmarks
et exemples livrés dans un paquet, sont ignorés.
- « Your code uses » (votre code utilise) est une correspondance statique de noms, fichier par
fichier : imports (noms réexportés compris), appels, lectures d'attributs et arguments nommés.
Il ne suit pas les accès dynamiques comme `getattr`, et il indique pour l'instant des fichiers,
pas des lignes ([#8](https://github.com/MohammadHijjawi97/since-cutoff/issues/8)). Un paramètre
devenu obligatoire, nommé seulement ou positionnel seulement est toujours marqué « uses this
API », jamais « old form », pour l'instant.
- « Your code uses » (votre code utilise) est une correspondance statique, fichier par
fichier : imports (noms réexportés compris), appels, lectures d'attributs et arguments nommés ;
le receveur d'un appel de méthode est typé d'après les annotations du paquet lui-même
(`df = pd.read_csv(...)` est un `DataFrame` ; une classe compte avec ses classes de base, à
travers vos paquets épinglés). Il ne suit pas les accès dynamiques comme `getattr`, et il
indique pour l'instant des fichiers, pas des lignes
([#8](https://github.com/MohammadHijjawi97/since-cutoff/issues/8)). Une méthode lue sur une
valeur dont le code ne montre pas la classe peut compter par son seul nom, quand ce nom
n'appartient qu'à une seule API modifiée et n'est ni un nom courant ni un membre d'un type
intégré ou de la bibliothèque standard : une telle correspondance `[name match]`
n'apparaît dans aucun rapport, ni dans les notes, ni dans `--fail-on` ou `--annotate` sauf
avec `--include-name-matches` (`include_name_matches` par MCP), car la plupart sont une
méthode homonyme d'une autre bibliothèque. Un paramètre devenu obligatoire, nommé seulement
ou positionnel seulement est toujours marqué « uses this API », jamais « old form », pour l'instant.
- Une note ne nomme un remplaçant que si le texte de dépréciation de la bibliothèque elle-même
l'énonce. Un conseil qui ne figure que dans un guide de migration (le
[MIGRATION.md](https://github.com/anthropics/anthropic-sdk-python/blob/main/MIGRATION.md)
Expand Down
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -794,11 +794,19 @@ meaning the same as `checks.example_type_checks`).
`__getattr__`, but `run` does not probe them.
- The diff covers the public API: `_private` names, and test suites, benchmarks and examples
shipped inside a package, are skipped.
- "Your code uses" is a static name match, file by file: imports (re-exported names included),
calls, attribute reads and keyword arguments. It does not follow dynamic access such as
- "Your code uses" is a static match, file by file: imports (re-exported names included),
calls, attribute reads and keyword arguments, with the receiver of a method call typed from
the package's own annotations (`df = pd.read_csv(...)` is a `DataFrame`; a class counts with
its base classes, across your pinned packages). It does not follow dynamic access such as
`getattr`, and it names files, not lines, for now
([#8](https://github.com/MohammadHijjawi97/since-cutoff/issues/8)). A parameter that became
required, keyword-only or positional-only is always "uses this API", never "old form", for now.
([#8](https://github.com/MohammadHijjawi97/since-cutoff/issues/8)). A method read on a value
whose class the code does not show can count by its name alone, when that name is one
changed API's and neither a common one nor a member of a built-in or standard-library type:
such a `[name match]` is hidden from every report, the notes, `--fail-on` and `--annotate`
unless `--include-name-matches` is given (`include_name_matches` over MCP), as most are
another library's method of the same name. A parameter that became
required, keyword-only or positional-only is always "uses this API", never "old form", for
now.
- A note names a replacement only when the library's own deprecation text states it. Advice that
is only in a migration guide (anthropic's
[MIGRATION.md](https://github.com/anthropics/anthropic-sdk-python/blob/main/MIGRATION.md)
Expand Down
2 changes: 1 addition & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,7 @@ AGENTS.md 中的区块包含带标签的说明条目,以及每个包的说明
- 目前仅支持 Python。下一步是 TypeScript(`.d.ts` 对比、`tsc`)([#1](https://github.com/MohammadHijjawi97/since-cutoff/issues/1))。
- 类型检查器能发现错误的名称、错误的参数和 PEP 702 弃用,但看不到签名不变而行为改变的情况,也看不到只在运行时发出警告的弃用。`scan` 还会列出用库自己的装饰器(名称包含 "deprecat")声明的弃用,以及已被移除、但模块仍通过 `__getattr__` 提供并发出警告的名称;不过 `run` 不会探测这些弃用。
- 对比只覆盖公开 API:`_private` 名称,以及包内自带的测试、基准测试和示例,都会被跳过。
- “Your code uses”(你的代码用到了)是逐个文件的静态名称匹配:导入(包括重新导出的名称)、调用、属性读取和关键字参数。它不追踪 `getattr` 这类动态访问,目前只指出文件,不指出行号([#8](https://github.com/MohammadHijjawi97/since-cutoff/issues/8))。变为必需、仅限关键字或仅限位置的参数,目前总是显示为“uses this API”,从不显示为“old form”。
- “Your code uses”(你的代码用到了)是逐个文件的静态匹配:导入(包括重新导出的名称)、调用、属性读取和关键字参数;方法调用的接收者会根据包自身的注解确定类型(`df = pd.read_csv(...)` 是一个 `DataFrame`;一个类连同它的基类一起计入,跨越你锁定的各个包)。它不追踪 `getattr` 这类动态访问,目前只指出文件,不指出行号([#8](https://github.com/MohammadHijjawi97/since-cutoff/issues/8))。在代码没有显示其类的值上读取的方法,可以只按名称计入:这个名称仅属于一个有变更的 API,既不是常见名称,也不是内置类型或标准库类型的成员。除非给出 `--include-name-matches`(通过 MCP 时为 `include_name_matches`),这种 `[name match]` 不会出现在任何报告、说明、`--fail-on` 和 `--annotate` 中,因为它们大多是另一个库中同名的方法。变为必需、仅限关键字或仅限位置的参数,目前总是显示为“uses this API”,从不显示为“old form”。
- 只有当库自己的弃用说明明确给出替代项时,说明才会写出替代项。只出现在迁移指南里的建议(anthropic 的 [MIGRATION.md](https://github.com/anthropics/anthropic-sdk-python/blob/main/MIGRATION.md) 建议对仍接受 `temperature` 的旧模型使用 `extra_body`)不会出现在说明中。
- 探测的是破坏性变更中按优先级排序的一个**样本**(你的代码已经用到的符号优先),而不是全部。
- “对照版本”指截止日期当天或之前发布的最新版本。模型对最近的版本了解得更少,所以实际的过时可能开始得更早。
Expand Down
40 changes: 38 additions & 2 deletions docs/how-it-works.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,20 @@ the files that import the package, and statically (nothing is run):
when the module lists the name in `__all__`);
- a method or attribute where the file reads it on the class, or on what the file shows is an
instance of the class (`client = anthropic.Anthropic(); client.messages.create(...)`), or on
`self` or `super()` in a subclass of it;
`self` or `super()` in a subclass of it; or on what the pinned releases' own annotations show
is an instance: a name assigned from a call of a package function or class (`df =
pd.read_csv(...)`: `read_csv` returns a `DataFrame`; `llm = ChatOpenAI(...)`), each step of a
chain (`df.groupby(...).count()` is `DataFrameGroupBy.count`), a parameter or variable annotated
with a package class (a string annotation too), and the returns of the file's own functions. A
class counts with its base classes, across the project's packages whose sources the scan read:
`ChatOpenAI` from langchain-openai is a langchain-core `BaseChatModel`, so `llm.predict(q)` is
`BaseChatModel.predict` although the file never imports langchain-core. A member is read up to
the nearest class that defines it in the pinned release, or that the diff says changed it:
`df.sum()` on a `DataFrame` is `DataFrame.sum`, which pandas 3 still defines, not the
`NDFrame.sum` whose parameters became keyword-only, and `llm.predict` is not also its base's
`BaseLanguageModel.predict`. The return annotations, bases, members and re-exports come from
the pinned release's sources (griffe; nothing is run) and are cached per release: the first
scan of a release reads its sources once more for them, a few seconds for pandas;
- a constructor where the class is called, under any name of the class;
- a parameter only where it is passed by keyword to that very callable: `temperature=` passed to
another library's `create` in the same file does not count. An SDK's beta mirror
Expand All @@ -155,11 +168,34 @@ the files that import the package, and statically (nothing is run):
A change from a diff that recorded no import paths (an older cache, or a change made by hand
through the Python API) is matched by package and name, and the report says "matched by name".

Last, a method or attribute read on a bare name the file shows nothing of (a parameter without
an annotation, a loop variable; not a literal, nor a value of the file's own classes and
functions, of a relative import, of a builtin or of a package the scan did not read) counts by
its name alone, when that name belongs to exactly one changed API across the changed packages
the code imports or reaches, is not a common Python name (`get`, `set`, `run`, `create`,
`close`, `update`, `count`, `sum`, `shape`, `view`, `to_dict`, ...) and is no member of a
built-in or standard-library type (`union` of a set, `fromisoformat` of a datetime, `is_integer`
of a float: every name `dir()` gives for dict, list, str, set, bytes, the numbers, tuple, the
`datetime` types, `pathlib.Path`, `re` patterns and matches, `logging.Logger`, text files and
`argparse`). A name the file assigns from a value of its own or of a builtin is not unknown
either: an alias (`alias = loc`), `store = self.store` after `self.store = Store()`, tuple
unpacking, `:=` and `except ... as err`.

Such a name match is hidden by default: no report (the terminal, report.md, the JSON lists, the
MCP answers), no note and no count of `--fail-on` or `--annotate github` has it, as on ordinary
code most of them are another library's method of the same name. `--include-name-matches` (on
`scan` and `sync`; `include_name_matches` for the MCP `project_changes`) shows them: the
reports tag each such use `[name match]` (the terminal next to its label and its place, the
Markdown summary in the form column, the JSON as `"match": "name_only"`), its note is written,
and CI counts it. CI counts per use: an API matched through a path in one file does not bring
its name-only places in other files along. The block does not keep the choice, so give the flag
on every sync.

Each use is one of two **forms**:

| form | meaning |
|---|---|
| **old form** | the code uses the API as the release at the cutoff allowed and the pinned release no longer does: it reads or imports what was removed, moved (its old path) or changed kind, uses what is deprecated, or passes a removed or deprecated parameter by keyword |
| **old form** | the code uses the API as the release at the cutoff allowed and the pinned release no longer does: it reads or imports what was removed, moved (its old path) or changed kind, uses what is deprecated, or passes a removed or deprecated parameter by keyword. A class that became a function, or the reverse (pandas 3's `option_context`), is called the same way, so a call of it is "uses this API" unless it passes a keyword the new signature does not take (a subclass or an `isinstance()` of it stays the old form); the note still says to check its signature |
| **uses this API** | the code is fine today, but an assistant editing it may write the old form; a parameter that is now required, keyword-only or positional-only is always this, since a name match cannot tell whether a call passes it the new way |

The terminal and the MCP tool `project_changes` show at most 3 files per API (`scan -v` shows
Expand Down
Loading
Loading