chore(deps): update python minor/patch #178
No reviewers
Labels
No labels
area:ai
area:ci-cd
area:notifications
area:observability
area:public-pages
backlog
bug
duplicate
enhancement
help wanted
invalid
question
security
type:decision
type:feature
type:infra
type:maintenance
type:security
v1.0.1
v1.1.0
v1.2.0
v1.3.0
v2.0.0
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
rbrooks/WeatherBot!178
Loading…
Reference in a new issue
No description provided.
Delete branch "renovate/python-minorpatch"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
This PR contains the following updates:
==1.18.5→==1.20.1==0.31.0→==0.32.0==1.7.2→==1.9.5==0.140.7→==0.162.2==2.14.2→==2.16.2==9.1.1→==9.2.0==1.2.2→==1.2.4==0.0.32→==0.0.33==0.16.10→==0.17.4==2.0.51→==2.1.8==1.8.0→==1.10.0==8.2.5→==8.3.0==9.10.9→==9.13.0==0.51.0→==0.54.0Release Notes
MagicStack/asyncpg (asyncpg)
v0.32.0Compare Source
Improvements
Support Python 3.15 (#1367, #1378)
(by @elprans in
1b11846and58c0c59)Support PostgreSQL 19 (#1379)
(by @elprans in
8d2a5ae)Make pool
min_sizea maintained connection floor and addinit_sizetocontrol the number of connections opened at startup (#1323).
Idle connections at or below
min_sizeare retained, and closed connectionsare replaced in the background.
init_sizedefaults tomin_size.To preserve the previous behavior, where the pool could drain to zero,
replace
min_size=Nwithinit_size=N, min_size=0.(by @shatilov-diman in
67a1cbc)Use
hashlib.pbkdf2_hmac()for faster SCRAM password derivation (#1359)(by @twelfthlabor in
00319a9)Use the statement cache for
copy_records_to_table()introspection (#1360)(by @matemax in
dd05865)Fixes
Clean up connections rejected by
target_session_attrsimmediately so anunresponsive rejected host cannot delay the selected connection, and cleanup
tasks cannot outlive the event loop. Rejected connections are aborted without
waiting for a graceful disconnect; servers may log "unexpected EOF on client
connection" if the Terminate message is not delivered before the transport
closes.
Fix a connection pool deadlock when releasing a connection after a protocol
abort (#1324)
(by @pchaseh in
4a83cc2)Honor
Connection.close(timeout=...)while waiting for a pendingcancellation (#1361)
(by @gyanu2507 in
c06bcb4)Prevent stuck cancellation from blocking connection teardown or pool release
by bounding cancellation with the command or release timeout (#1370)
(by @elprans in
4cf0220)Preserve the server error when the connection closes mid-operation (#1369)
(by @RitiGrover in
6c8f1a7)Fix COPY cleanup when a records generator raises, preserving the original
exception and avoiding protocol desynchronization and stalled rollback (#1358)
(by @CodingCossack in
fdc6bc8)Reparse unnamed statements before cursor binding when the statement cache
is disabled (#1364)
(by @elprans in
302ba49)Allow cursors in transactions started outside
Connection.transaction(),such as with an explicit
BEGIN(#1350)(by @aryansk in
25aaaf5)Remove the query logger when the
query_logger()block raises (#1373)(by @kratos0718 in
6a7fbd3)Fix a segfault in
Record.get()with an invalid number of positionalarguments (#1333)
(by @apoorvdarshan in
40a3832)Align SSL startup retries with libpq and improve SSL rejection diagnostics
(#1366). Retry alternative transports only before authentication succeeds.
Direct TLS now requires
sslmode=require,verify-ca, orverify-full,or an explicit
SSLContext.(by @elprans in
78752c9)Preserve password errors during SSL fallback when the fallback fails with
a less specific authorization error (#1331)
(by @Mauricio0129 in
2d97947)Handle cleartext password authentication when no password is supplied
without raising an
AttributeError(#1351)(by @aryansk in
6be5915)Set the
postgresqlALPN protocol on SSL contexts created by asyncpg sodirect TLS connections work with PostgreSQL 17 and later (#1372).
User-provided contexts must set this protocol for direct TLS connections.
(by @DurandA in
b21325d)Link explicitly against
libmon non-Windows platforms to fix undefinedmath symbol errors when loading the extensions (#1305)
(by @veeceey in
db8ecc2)Fix builds on FreeBSD 15 by linking against
libstdthreads(#1368)(by @elprans in
87bcaf9)Other
Replace
pkg_resourceswithpackaginginsetup.py(#1362)(by @elprans in
6695db7)Build Linux and macOS ARM64 wheels on native runners and add Windows ARM64
wheel builds (#1308)
(by @ddelange in
1e8cc1c)Add manual and PR-label-triggered dry runs to the release workflow (#1374)
(by @elprans in
1bd643e)Clarify how
execute()uses prepared statements and how to confirm anINSERTin the documentation (#1336)(by @pctablet505 in
cdf816c)Fix typos in comments and docstrings (#1332)
(by @maxtaran2010 in
bc3e246)authlib/authlib (authlib)
v1.9.5Compare Source
Changelog: https://docs.authlib.org/en/stable/upgrades/changelog.html#changelog-1-9-5
Full Changelog: https://github.com/authlib/authlib/compare/v1.9.4...v1.9.5
v1.9.4Compare Source
Changelog: https://docs.authlib.org/en/stable/upgrades/changelog.html#changelog-1-9-4
Full Changelog: https://github.com/authlib/authlib/compare/v1.9.3...v1.9.4
v1.9.3Compare Source
Changelog: https://docs.authlib.org/en/stable/upgrades/changelog.html#changelog-1-9-3
Full Changelog: https://github.com/authlib/authlib/compare/v1.9.2...v1.9.3
v1.9.2Compare Source
Changelog: https://docs.authlib.org/en/stable/upgrades/changelog.html#changelog-1-9-2
Full Changelog: https://github.com/authlib/authlib/compare/v1.9.1...v1.9.2
v1.9.1Compare Source
Changelog: https://docs.authlib.org/en/stable/upgrades/changelog.html#changelog-1-9-1
What's Changed
Full Changelog: https://github.com/authlib/authlib/compare/v1.9.0...v1.9.1
v1.9.0Compare Source
What's Changed
New Contributors
Full Changelog: https://github.com/authlib/authlib/compare/v1.8.0...v1.9.0
v1.8.0Compare Source
What's Changed
id_token_signed_response_algclient metadata to guess algs by @azmeuk in #888New Contributors
Full Changelog: https://github.com/authlib/authlib/compare/v1.7.2...v1.8.0
fastapi/fastapi (fastapi)
v0.162.2Compare Source
Docs
Internal
v0.162.1Compare Source
Docs
Translations
Internal
v0.162.0Compare Source
Omitted
boolandbool | Noneform fields now use their declared defaults or produce missing-field errors, consistent with other fields and Pydantic behavior. FastAPI no longer infersFalsefor omitted booleans. This applies to individual form parameters, form models, and unions of form models.Omitted boolean model fields are excluded from
model_dump(exclude_unset=True), and their defaults no longer count as submitted fields during union selection. Default factories andvalidate_defaultfollow the usual rules.For HTML checkboxes, declare a boolean with a default of
False. See Boolean Form Fields.Breaking Changes
v0.161.1Compare Source
Fixes
v0.161.0Compare Source
Features
v0.160.0Compare Source
Query, header, cookie, and form parameter models now apply their
extraconfiguration to all submitted fields in the same parameter location, consistent with Pydantic behavior. This includes fields declared in other models, individual parameters, or dependencies, which were previously excluded.With
extra="forbid", fields outside the model are rejected even if another parameter declares them. Withextra="allow", those fields are included as model extras. With the defaultextra="ignore", they are ignored.Breaking Changes
extraconfiguration to sibling fields. PR #16594 by @tiangolo.Internal
v0.159.1Compare Source
Features
v0.159.0Compare Source
Features
v0.158.2Compare Source
Features
v0.158.1Compare Source
Fixes
v0.158.0Compare Source
Features
v0.157.0Compare Source
Features
Docs
Internal
v0.156.8Compare Source
Fixes
bytes. PR #16566 by @tiangolo.v0.156.7Compare Source
Fixes
AliasChoices. PR #16564 by @tiangolo.v0.156.6Compare Source
Fixes
v0.156.5Compare Source
Fixes
AliasChoicesfor path parameters. PR #16560 by @tiangolo.v0.156.4Compare Source
Fixes
AliasChoicesfor named JSON body parameters. PR #16559 by @tiangolo.AliasChoicesextraction for form models. PR #16557 by @tiangolo.v0.156.3Compare Source
Fixes
AliasChoicesfor query parameters, headers, and cookies. PR #16555 by @tiangolo.v0.156.2Compare Source
Fixes
v0.156.1Compare Source
Fixes
v0.156.0Compare Source
Fixes
Field()attributes in request parameters. PR #16547 by @tiangolo.v0.155.0Compare Source
The deprecated
**extrakeyword arguments have been removed fromPath,Query,Header,Cookie,Body,Form, andFile. Unknown keyword arguments now raiseTypeError. For additional JSON Schema properties, replace, for example,Query(custom="value")withQuery(json_schema_extra={"custom": "value"}).Breaking Changes
**extrafrom request parameters. PR #16545 by @tiangolo.v0.154.0Compare Source
Features
v0.153.0Compare Source
Unions of form models now apply the same browser form handling as individual models. Empty scalar fields use defaults or produce missing-field errors, empty list items are preserved, omitted boolean fields become
False, and unselected files are treated as missing. JSON request bodies are unchanged.Fixes
v0.152.0Compare Source
Validation errors for header parameter models now report the HTTP header name, such as
int-val, instead of the Python field name,int_val. Explicit aliases,Header(convert_underscores=False), and Pydantic models withloc_by_alias=Falsekeep their existing behavior.Fixes
v0.151.0Compare Source
JSON request bodies now distinguish an explicit
nullfrom an omitted value. For individualBodyparameters,nullis validated asNone: nullable types accept it, and non-nullable types reject it even when a default is defined. Defaults and default factories apply only when the value is omitted. This applies both to parameters inside JSON objects and to parameters representing the entire body. Clients that usednullto request a default should omit the value instead.When a JSON object is expected because of
Body(embed=True)or multiple body parameters, sendingnullas the entire body now returns a validation error.Breaking Changes
v0.150.0Compare Source
When no file is selected, browsers submit a file part with an empty filename and empty content. FastAPI now treats this as missing: required file fields return a validation error, and fields with defaults or default factories use them. Unselected inputs are ignored in lists of files. Named zero-byte files are still accepted.
Breaking Changes
v0.149.0Compare Source
Browsers submit empty text inputs as empty strings (
""). FastAPI now treats empty scalar form values as missing in form models, matching individualFormparameters. Required fields return a validation error. Fields with defaults or default factories use them and remain unset inmodel_dump(exclude_unset=True).Empty strings inside lists are preserved. Omitted boolean form fields still become
False. JSON requests and direct Pydantic validation are unchanged.Breaking Changes
v0.148.0Compare Source
Defaults for omitted
Query,Header,Cookie, andFormparameters and model fields are no longer treated as user input. Defaults are not validated unlessvalidate_default=Trueis enabled. If you rely on validators running for omitted values, enable default validation withField(validate_default=True)for model fields or, for example,Query(validate_default=True)for individual parameters.Body(validate_default=True)is now honored too.For parameter models, applying a default no longer marks a field as explicitly set, so
model_dump(exclude_unset=True)excludes it. Default factories run once, and validation errors no longer include injected defaults ininput.Omitted boolean form fields still become
Falseand count as explicitly set.Breaking Changes
v0.147.0Compare Source
Omitted
boolandbool | Noneform fields are now interpreted asFalse, matching unchecked HTML checkboxes. This applies to individualFormparameters and form models, including required fields and fields with defaults ofTrueorNone. The inferred value is considered explicitly set, including inmodel_dump(exclude_unset=True).JSON requests and direct Pydantic validation are unchanged. See Boolean Form Fields.
Breaking Changes
Internal
v0.146.0Compare Source
The
root_pathparameter inFastAPI()is deprecated. Configure the server withfastapi run --root-path /api/v1instead.Breaking Changes
root_pathparameter inFastAPI. PR #16520 by @tiangolo.v0.145.0Compare Source
Breaking Changes
openapi_prefixand unused**extrafromFastAPI. PR #16518 by @tiangolo.Docs
v0.144.0Compare Source
Features
v0.143.5Compare Source
Fixes
bytefile list parameter. PR #9383 by @Viicos.v0.143.4Compare Source
Upgrades
v0.143.3Compare Source
Fixes
Docs
v0.143.2Compare Source
Fixes
Translations
Internal
v0.143.1Compare Source
Fixes
iter_route_contexts(). PR #16484 by @tiangolo.v0.143.0Compare Source
Automatic OpenTelemetry exporter setup is now opt-in. If you rely on FastAPI to configure exporters from
OTEL_*environment variables, setFASTAPI_OTEL_AUTO_CONFIGURE=trueor useFastAPI(telemetry={"auto_configure": True})to retain the previous behavior.Applications that configure their own providers and exporters require no changes. FastAPI Cloud enables automatic setup for you.
Breaking Changes
v0.142.4Compare Source
Fixes
v0.142.3Compare Source
Fixes
Docs
Translations
Internal
bump-pre-commit-hooksworkflow. PR #16463 by @YuriiMotov.gpt-6-astramodel for translations. PR #16453 by @YuriiMotov.v0.142.2Compare Source
Fixes
v0.142.1Compare Source
Fixes
v0.142.0Compare Source
Features
Refactors
Docs
Translations
Internal
setup-uvaction to10.0.1. PR #16249 by @YuriiMotov.commit_in_placeinput intranslate.yml. PR #16216 by @YuriiMotov.zizmor.ymlworkflow (main->master). PR #16178 by @YuriiMotov..cacheinbuild-docs.yml. PR #16156 by @YuriiMotov.v0.141.1Compare Source
Fixes
app.frontend(). PR #16105 by @tiangolo.Docs
FASTAPI_ENVin FastAPI CLI guide. PR #16104 by @tiangolo.v0.141.0Compare Source
Features
app.frontend(check_dir="auto"), to make local development more convenient withfastapi dev. PR #16102 by @tiangolo.v0.140.13Compare Source
Fixes
status_codebeing ignored for SSE and JSONL streaming endpoints. PR #15937 by @SAURABHSALVE.Docs
format_sse_eventdocstring rendering of\n\nterminator. PR #15613 by @AshNicolus.v0.140.12Compare Source
Fixes
format_sse_eventto comply with SSE spec. PR #15515 by @Zawwarsami16.v0.140.11Compare Source
Fixes
response_model_*params ignored for non-generator endpoints withIterable[..]return type. PR #15093 by @YuriiMotov.v0.140.10Compare Source
Fixes
Internal
v0.140.9Compare Source
Fixes
exclude_defaultsnot propagated to dict keys and values injsonable_encoder. PR #16043 by @MBGrao.Internal
v0.140.8Compare Source
Fixes
include_router(). PR #15077 by @alex-raw.pydantic/pydantic-settings (pydantic-settings)
v2.16.2Compare Source
v2.16.1Compare Source
Highlights
This patch release fixes two CLI regressions introduced in 2.16.0.
Bug fixes
CLI
CliPositionalArg[tuple[int, int]]andNamedTuples are single-valued again (#1069). Since 2.16.0 they were treated as variadic, so a CLI either refused to start with aSettingsErroror put the wrong argument into the tuple. Variable-length tuples (tuple[int, ...]and bareTuple) are still variadic.type ListOfDict = list[dict[str, int]], accepts a JSON list again (#1049). Since 2.16.0,--xx '[{"a":1},{"b":2}]'was merged into the single dict{"a": 1, "b": 2}and failed validation.All changes (including dependency bumps and internal maintenance)
What's Changed
Full Changelog: https://github.com/pydantic/pydantic-settings/compare/v2.16.0...v2.16.1
v2.16.0Compare Source
Highlights
Breaking changes and behavior changes
tomlifallback is gone in favor of stdlibtomllib; thetomlextra is kept as an empty extra sopip install pydantic-settings[toml]keeps working.python-dotenv>=1.2.2is now required (#971) to pick up the fix for CVE-2026-28684.validate_by_alias=False(#1033). Env vars, secrets, CLI flags and defaults now use only field names, matching pydantic. An env var named after the alias is now ignored instead of raisingextra_forbidden, and the CLI exposes--applerather than--pomo.validation_aliastakes precedence overalias(#1038). When the two differ, values from JSON/YAML/TOML files and the CLI were silently ignored; they now load. CLI help lists thevalidation_aliasflag first, andCliApp.serializeemits it.case_sensitive=False, args after--and those captured byCliUnknownArgskeep their case (-Xmx4Gno longer becomes-xmx4g). Unrecognized options are now reported as typed (--NOPEinstead of--nope).json_file_encoding/yaml_file_encodingstill override it.nested_model_default_partial_update=True: the default is only dropped when the incoming value selects a different union member.New features
CliApp.runandvalidate_by_alias=Falsehandling; both are fixed.Settings.settings_cached()andSettings.settings_clear_cache()(#952). These give you a process-local singleton per settings class without hand-rollinglru_cachewrappers. Calling the class directly still builds a fresh instance.AWSSystemsManagerSettingsSource(#941). It recursively reads every parameter under anssm_path, with/mapping onto nested models. Install it with the newaws-systems-managerextra.env_file_depth(#940) lets a relativeenv_filebe found in parent directories, mirroringpyproject_toml_depth. This is useful when scripts run from a subdirectory of a monorepo. It defaults to0, which keeps the current behavior.CliVariadicArgfor named variadic options (#949, #958). It accepts--param a b cin addition to repeated--param a --param b. Required fields need at least one value.Bug fixes
CLI
CliApp.serializeoutput now round-trips throughCliApp.runfor enums, tuples, dates andDecimals (#988), explicitNonevalues (#981), empty containers (#1008),CliVariadicArgvalues (#976) and populatedCliUnknownArgsfields, which previously raisedIndexError(#992).--items []) now give an empty list instead of[''](#994).frozenset,MutableSequenceand other collection subtypes are now treated as repeatable/variadic options (#972), and so are named collection type aliases such asTypeAliasType('Addrs', list[IPvAnyAddress])(#1032).cli_exit_on_error=Falseis now honored inCliSubCommandparsers, which raiseSettingsErrorinstead of exiting (#977).CliSubCommandaliases keep their case withcase_sensitive=False(#989).CliUnknownArgsfield now receives only its own subcommand's leftover args (#1004).CliApp.format_helpno longer crashes whencli_prefixis set inmodel_config(#945).Type aliases and parsing
TypeAliasType('V', list[int] | str)) now decode like their inline equivalents in env and dotenv sources, including underOptional,Annotatedand custom validation aliases (#1006, #1030, #1041).Annotatedtype alias no longer mutates sharedFieldInfometadata, which made validators run twice on subclasses or aftermodel_rebuild()(#979).AliasPath('SOME_KEY')no longer tries to JSON-decode plain string values (#980).collections.abc.Set/MutableSet/typing.AbstractSetfields now JSON-decode env values (#973).typing.Dictfield withenv_nested_delimiterno longer raisesIndexError, which was a regression in 2.13.0 (#1009).Dotenv, secrets and config files
extra='allow', a non-lowercaseenv_prefixis now stripped from dotenv extras (#959, #968).secrets_case_sensitive=Trueis now honored on Windows (#1001).~paths are now expanded when checking that the file exists (#1034), and an explicittoml_table_header=()now overrides the model config to read the file root (#1027).Cloud sources
env_parse_none_str(#1019, #1040).dash_to_underscore=Truenow works for nested model fields (#1025).NestedSecretsSettingsSourcenow validates its specific constructor arguments (#975).DotenvFilteringis exported frompydantic_settings.sources.types(#944).Documentation
strict,extra,context) passed tomodel_validate()are not applied toBaseSettings, and give a workaround (#1017).extra='forbid'doesn't reject misspelled top-level env vars (#986).populate_by_name(#933), and add a nested model example forenv_prefix_target(#953).All changes (including dependency bumps and internal maintenance)
What's Changed
NestedSecretsSettingsSourcespecific arguments by @makukha in https://github.com/pydantic/pydantic-settings/pull/975CliApp.serializeoutput parseable for enum and non-JSON values by @SulimanAbdulrazzaq in https://github.com/pydantic/pydantic-settings/pull/988CliApp.serializefor models withCliUnknownArgsfields by @truecallerabreham in https://github.com/pydantic/pydantic-settings/pull/992secrets_case_sensitiveon Windows by overriding_load_env_varsby @hramezani in https://github.com/pydantic/pydantic-settings/pull/1001samuelcolvin/check-python-versionaction to v5.1 by @Viicos in https://github.com/pydantic/pydantic-settings/pull/1014validate_by_alias=Falseon pydantic 2.14 by @maharanay22 in https://github.com/pydantic/pydantic-settings/pull/1022validate_by_alias=Falseby @hramezani in https://github.com/pydantic/pydantic-settings/pull/1033New Contributors
Full Changelog: https://github.com/pydantic/pydantic-settings/compare/v2.15.0...v2.16.0
v2.15.0Compare Source
Highlights
Behavior changes
case_sensitivenow applies to init kwargs and config-file sources (#900).InitSettingsSourceand the JSON/TOML/YAML config sources previously ignoredcase_sensitive. Since it defaults toFalse, case-insensitive matching is now the default for these sources — e.g.Settings(TeSt=...)now populates atestfield where it previously did not. Nested keys are still matched case-sensitively.IncompleteFieldDefinitionWarningtelling you to callmodel_rebuild(). If you havefilterwarnings = errorconfigured, this may surface as a new failure.ValidationError(#926) instead of a less specific error.New features
cli_show_env_vars=True(#860), so generated--helpoutput doubles as configuration documentation.PYDANTIC_SETTINGS_DEBUGfor debugging settings resolution (#906, #913). Set it to a truthy value withDEBUGlogging enabled to see each source's contribution in priority order, which source won for each value, and whichenv_file/secret files were probed, loaded, or skipped — the long-standing "why isn't my.envbeing picked up?" question.toml_table_headerfor regular TOML files (#882, #886, #887), letting you root settings at a nested table in any TOML file, not justpyproject.toml.Traversablesupport for JSON/TOML/YAML file sources (#902), so you can load config packaged inside a distribution — including files inside a zip or wheel — viaimportlib.resources.files(...)without casting toPath.project_idcan come from an earlier settings source (#878), rather than only from the constructor orGOOGLE_CLOUD_PROJECT.Bug fixes
case_sensitive=True(#894). Windows upper-casesos.environkeys, so fields raisedField requiredinstead of picking up their values.AliasPathon nested model fields not JSON-decoding env values (#898).dbx_tokenbeing swallowed by adb: dictfield.nested_model_default_partial_update=Truecorrupting discriminated unions (#876).Secretsubclasses crashing when loaded from the environment (#920).Optional[Annotated[MyEnum, ...]]withenv_parse_enums=True(#910).yaml_config_sectionnow falls back to defaults instead of raisingAttributeError: 'NoneType' object has no attribute 'keys'(#914).NestedSecretsSettingsSourceno longer follows symlinks pointing outsidesecrets_dir(#889).list_secretscall whencase_sensitive=True(#862), lowering the required IAM permissions to justroles/secretmanager.secretAccessor.types-boto3[secretsmanager]is no longer required at runtime (#880).Documentation
All changes (including dependency bumps and internal maintenance)
What's Changed
New Contributors
Full Changelog: https://github.com/pydantic/pydantic-settings/compare/v2.14.1...v2.15.0
pytest-dev/pytest (pytest)
v9.2.0Compare Source
pytest 9.2.0 (2026-10-27)
Removals and backward incompatible breaking changes
#14716:
-cnow raises a usage error for a path that does not exist, is a directory, or is in a format pytest has no loader for. Such paths were previously either ignored or reported as an unhandledFileNotFoundError.#8593: The
indirect,idsandscopearguments ofMetafunc.parametrize() <pytest.Metafunc.parametrize>-- and therefore of@pytest.mark.parametrize <pytest.mark.parametrize ref>-- are now keyword-only.Passing them positionally was a common source of confusing errors, e.g.
parametrize("arg1", "arg2", [(1, 2)])made the argvalues land inindirectand failed withindirect fixture '(1, 2)' doesn't exist; such mistakes now fail with an understandableTypeError. The type annotations have always declared these arguments as keyword-only.#993: Plugin import failures at startup are now reported and classified instead of escaping as a raw traceback with the accidental exit code
1:-p,pytest_pluginsorPYTEST_PLUGINS-- exits withpytest.ExitCodeUSAGE_ERROR(4), like aconftest.pythat fails to import;pytest11entry point or a missing plugin dependency -- exits withpytest.ExitCodeINTERNAL_ERROR(3), with the traceback preserved.pytest.mainnow returns these codes instead of raisingImportError. Also fixed anIndexErrorfrom pytest's own internals when the plugin raised an exception with no arguments.Deprecations (removal in next major release)
#14742: The private
_pytest.python.CallSpec2name has been renamed toCallSpec.The old
CallSpec2alias remains available for now and will be removed in pytest 10 or 11.#14755: Schedule yield_fixture decorator for removal in pytest 10 or 11.
#14774: Similarly to the deprecation of class-scoped fixtures defined as instance methods in pytest 9.1,
all higher-scoped fixtures (
module,package, andsession) definedas instance methods are now deprecated.
They suffer from the same issue the original deprecation only fixed for
class-scoped fixtures, see
10819and14011.#14808: Passing a non-string value to a
string-typed ini option now emits apytest.PytestRemovedIn10Warning. This affects[tool.pytest.ini_options]inpyproject.toml. In pytest 10 this will raise aTypeError, matching the behavior of the corresponding TOML config path.#7323: Passing
bytestoTerminalReporter.write_line()is now deprecated, pass astrinstead.New features
strict_markersis enabled, marker names used in-mexpressions are now validated against registered markers.parametrize_long_str_id_strategyconfiguration option to control how longstr/bytesparameter values are handled in auto-generated test IDs. Explicit IDs set viaids=[...]orpytest.param(..., id=...)are not affected. Available strategies:short(default, values over 100 chars fall back toargname+index),sha256(SHA-256 hex digest),legacy(keep full value), anddisallow(error requesting explicit IDs).Improvements in existing functionality
#10745: A
RecursionErrorwhose recursion origin cannot be located now shows only thefirst and last 10 stack frames, matching what pytest already did when locating
the origin failed outright. Previously the full traceback -- often around a
thousand near-identical frames -- was rendered, which was both slow and
unreadable. Pass
--full-traceto see every frame.#10745: The line numbers of a module's statements are now computed once per traceback
instead of once per traceback entry, which noticeably speeds up rendering
failures raised from large test modules.
#11418: The cached rewritten modules in
__pycache__are now invalidated by a hash ofthe source instead of its modification time, using the checked-hash pyc format
from
552.Timestamps make the cache useless wherever source files get fresh modification
times, most notably in CI: a checkout gives every file a new mtime, so a
restored cache is discarded in full and every test module is rewritten again.
Hashing the source costs roughly 15 microseconds per file and makes the cache
survive that.
Existing timestamp based caches are ignored and rewritten once.
#12624: The
pytest_pluginsvariable and thePYTEST_PLUGINSenvironment variable now also accept entry point names of installed plugins (e.g.xdist), in addition to importable module names, matching the behavior of the-pcommand-line option.As part of this change, plugins specified by entry point name through these mechanisms are now also listed in the
plugins:line of the terminal header (12615).Based on the original implementation by Marcel Telka in
12616.#12697: Warnings emitted while importing plugins through
-p,PYTEST_PLUGINS, or entry point autoloading are now captured, filtered, and reported like other warnings.#12860: Assertion diffs for
dict.items()anddict.keys()comparisons (>=,<=,>,<,==) now show which items are missing, the same way set comparisons do.#13354:
Node.get_closest_marker <_pytest.nodes.Node.get_closest_marker>now also accepts a~pytest.MarkDecoratoras itsdefault, for exampleitem.get_closest_marker("foo", pytest.mark.foo(1)), and returns the wrapped~pytest.Mark.Previously the only way to build a
defaultwas to instantiate~pytest.Markdirectly, which is private and warns.#13369: Improved the error from
pytest.RunResult.assert_outcomeswhen a plugin changes or removes pytest's standard terminal summary, and documented how to isolate output-changing plugins.#13431: Exposed
pytest.ParameterSetas the type of the return value ofpytest.param().This can be used in type annotations and
isinstancechecks.#13564: Issue a warning when fixtures are wrapped with a decorator that prevents
correct discovery, including
@classmethodabove@pytest.fixtureand@staticmethodabove@pytest.fixture. Put@pytest.fixtureabovethose decorators on test classes.
#14164: Exposed
pytest.Approxas the type of the return value ofpytest.approx().This can be used in type annotations and
isinstancechecks.#14463: Assertion diffs for mappings now match up keys the way the compared mappings match them themselves.
Key comparison goes through the mappings' key views instead of plain sets of their keys, so a mapping
with its own notion of key equivalence, such as case-insensitive header mappings, no longer
reports its common keys as missing on either side.
#14523: Large assertion comparison diffs are now built lazily and capped to the
truncation budget, so a huge diff is no longer formatted in full just to
be truncated. As a consequence, the truncation footer no longer
reports the exact number of hidden lines.
#14687: The default fd-level output capturing (
--capture=fd) now skips reading the capture buffer for tests which produce no output, reducing per-test overhead in large test suites.#14724:
--no-summarynow only applies directly to pytest's own summary.The flag no longer skips the
pytest_terminal_summaryhook, so third-partyplugins (for example coverage) can still write their terminal summaries.
#14751:
parser.addini <pytest.Parser.addini>now also accepts type expressions for itstypeargument, in addition to the existing string tags: the plain Python typesstr,bool,int,float, aLiteralof strings, and unions of these (for exampleint | strorint | Literal["auto"]).#14754:
pytest.approx()now rejects infinite relative tolerances fordatetime.timedeltavalues with a clearValueErrorinstead of raisingOverflowError.#14791: The
junit_family,junit_logging,junit_duration_report,console_output_style,empty_parameter_set_mark,assertion_text_diff_styleandtmp_path_retention_policyconfiguration options are now registered with the set of values they accept: an invalid value now fails with a clean usage error instead of being silently ignored or crashing later, andpytest --helplists the accepted values.config.getini <pytest.Config.getini>now raisespytest.UsageErrorinstead ofTypeErrororValueErrorwhen the value read from the configuration file does not match the registered type, so a bad value is always reported as a short usage error message instead of an internal error traceback. Asking for an option name that was never registered still raisesValueError.#15006:
pytest.approxnow emitspytest.PytestApproxDecimalToleranceWarningwhen arelorabstolerance is given as afloatthat cannot be represented exactly for a~decimal.Decimalcomparison, since such a tolerance is widened to the float's exact binary value.#14675, #14953, #15153, #15132: The
truncation_limit_lines,truncation_limit_chars,max_warnings,verbosity_assertions,verbosity_test_cases,verbosity_subtests,tmp_path_retention_count,log_level,log_cli_level,log_file_level,log_auto_indentandfaulthandler_timeoutconfiguration options now accept native integer (and, forlog_auto_indent, boolean) values in TOML configuration files, while still accepting strings for backward compatibility.log_file_modenow reports invalid values as a usage error.#15254:
tmp_pathandTempPathFactory.mktemp <pytest.TempPathFactory.mktemp>no longer list the whole base temporary directory on every call, which made sessions with many temporary directories quadratically slow. Numbers are now handed out from a per-prefix counter, skipping names that already exist.#4569:
Node.add_marker <_pytest.nodes.Node.add_marker>now stores a~pytest.Markinnode.keywords, matching what marks applied during collection store. Previously it stored a~pytest.MarkDecorator.#9436: Avoided formatting tracebacks for tests marked with
xfail(run=False).Bug fixes
#10128: Configured warning filters now apply to warnings emitted by
pytest_configurehooks.#10210:
pytest.approxnow raises aTypeErrorfor any nested container, notonly for one of the same type as its parent. Previously
approx([{"k": 1}])and
approx({"k": [1]})were accepted and then compared unequal to everyvalue, silently ignoring the tolerance, while
approx([[1]])correctlyreported that nesting is unsupported.
#10644:
monkeypatch.setattr()no longer leaves a new entry in the instance__dict__when it patches an attribute that the instance inherits from its class.Previously
undo()assigned the inherited value onto the instance, which shadowed the class attribute -- permanently freezing the result for descriptors that resolve dynamically.#1139: Fixed tracebacks showing
???instead of source lines for code compiled with a filename relative to a directory other than the current working directory -- e.g. compiled Cython modules, when running pytest from a subdirectory of the project root.The source lookup now falls back to searching
sys.pathfor such filenames, as the standardtracebackmodule does.#11502: The
rootdiris no longer derived from a configuration file that is not a regular file, such as--config-file=/dev/null.#11783: Plugins installed in editable mode (
660) are marked for assertion rewriting again.Such installs do not record the package's Python files in the distribution metadata, so pytest found nothing to rewrite and assertions inside the plugin lost their introspection. The plugin's entry point is now used to locate the package in that case.
#11933:
pytest.warnsno longer re-emits unmatched warnings withmodule="warnings", which causedmodule-based warning filters to be applied to the wrong warnings. Sincewarnings.WarningMessagedoes not record the module a warning was emitted with, re-emitted warnings now use the standardwarnings.warn_explicitdefault of deriving the module from the warning's filename.#12175:
ExceptionInfo.exconly(tryshort=True) <pytest.ExceptionInfo.exconly>now strips theAssertionError:prefix of failedassertstatements for~pytest.ExceptionInfoobjects created bypytest.raises, and failures insideunittest.TestCase.subTestreport the same short crash line as regular tests.#12223: The
messageattribute of<failure>and<error>elements in--junitxmlreports is now shortened to whole lines within 500 characters. When something is dropped, it ends with a note pointing to the element text, which still holds the full report. Previously, very verbose assertion output (e.g. with-vv) produced attributes tens of thousands of characters long, which some XML parsers fail on.#12307: Fixed truncation of long collections (lists, dicts, sets, etc.) in the "message" part of failed assertions (as in
assert condition, msg) when using-vv: the message now uses the same unlimited repr as the rest of the assertion output, instead of eliding collection elements with....#12824: Custom command-line options now fail with a clear error when their implicit
destination conflicts with an existing option, avoiding silent overrides of
built-in options such as
-k.#13292: Fixed a stale rewritten module being used when a test file is modified twice
within the same second without changing its size -- the pyc header could only
record whole-second timestamps. The cache is now keyed on a hash of the source.
#14036:
__tracebackhide__is now respected inExceptionGrouptracebacks, both for the group itself and for its sub-exceptions.#14189: Nested usage of
caplog.filtering <pytest.LogCaptureFixture.filtering>no longer removes filters early if they were already present.#14412: Fixed incorrect timing information for subtests and parent tests when
console_output_style = "times".#14445: Assertion rewriting no longer evaluates walrus operator (
:=) expressions more than once, which reported results the test never computed when the expression had side effects.An operand evaluated before a later walrus is now reported with the value it actually saw, so
assert value != identity(value := value.lower())keeps Python's left-to-right order.#14552: Fixed stale
co_filenamevalues after a test module or directory is moved. Assertion-rewrite caches are reused when the source hash still matches, and in-memory code objects are pointed at the current path.#14586: Negative values passed to
log_auto_indentnow correctly fall back to no indentation, matching the documented behavior.#14619: Fixed a crash in
pytest.mark.parametrizewhen a non-sequence value is passed for a parameter set. It now provides a clear error message during collection.#14635: Fixed fixture resolution failures when specifying multiple test paths on the command line that share common parent directories. Parametrized fixtures defined in a
conftest.pycould be silently dropped depending on the order of the specified paths.#14637: Pytest now correctly skips identical trailing characters in assertion text diffs even when the compared strings differ at the beginning.
#14638: Top-level options in
pytest.tomland.pytest.tomlfiles now raise an error instead of being silently ignored when no[pytest]table is present.#14683: Fixed a regression in pytest 9.1 where a conftest.py located in the rootdir was no longer visible to tests and doctests collected from outside the rootdir (for example when passing a parent directory of the rootdir as a collection argument, or setting
--rootdirto a subdirectory).As a result, fixtures defined in such a conftest -- including
doctest_namespaceinjections -- were not available, causing errors such asNameErrorin doctests.#14700: Fixed an internal error on Python 3.12 when a doctest on a fixture-decorated function was skipped.
#14705: TOML configuration files passed via
-cnow also read their configuration from the[pytest]table, the one pytest's own configuration files use. The[tool.pytest]and[tool.pytest.ini_options]tables such files were previously restricted to keep working; using both styles in one file is an error.#14828: Exception groups raised from another exception now print their cause chain only once in the traceback output.
#14864: Fixed collection on Windows collecting the whole suite instead of the given path, on file systems which do not support file IDs (
st_inois0for every file, as seen for example onsshfs-win/WinFsp mounts).#14884:
Cache.get(),Cache.set()andCache.mkdir()now reject keys that resolve outside the cache directory, and--cache-showignores glob matches outside it.#14909: Fixed
MonkeyPatch.delattr <pytest.MonkeyPatch.delattr>,MonkeyPatch.setitem <pytest.MonkeyPatch.setitem>andMonkeyPatch.delitem <pytest.MonkeyPatch.delitem>recording a stale undo entry when the underlying mutation failed, which could cause spurious errors during teardown. The undo entry is now recorded only after the mutation succeeds, matching the behavior ofMonkeyPatch.setattr <pytest.MonkeyPatch.setattr>.#14912:
~pytest.PytestWarninginstances defined in_pytest.deprecatedare no longer reused acrosswarnings.warncalls, so warnings raised as errors (e.g. with-W error) no longer accumulate stale traceback frames from previous raises.#14942: Fixed a regression in 9.1.0 that made collection very slow (quadratic) when many fixtures are defined with the same name.
This can happen particularly when many tests are defined in separate classes which inherit from a base class which defines fixtures (the fixtures are repeated per class).
#15005:
pytest.approxno longer treats finite~decimal.Decimalvalues outside thefloatrange (such asDecimal("1e400")) as infinite, which made in-tolerance comparisons fail.#15006: Restored the tolerance shown by
pytest.approxfor~decimal.Decimalvalues, which since 9.0 displayed???for the default tolerance and reported the rawrelratio instead of the absolute tolerance actually compared against.#15006: The failure message for
pytest.approxcomparisons of~decimal.Decimalcollections no longer reportsMax absolute difference: -inf, or crashes for mappings, when thedecimal.FloatOperationtrap is set.#15006:
pytest.approxno longer raisesAttributeErrorwhen comparingnumpy.ndarrayinstances withobjectdtype, such as arrays of~decimal.Decimal.#15032: Help texts for command-line options and configuration options now keep their indentation
and blank lines. Previously each line was stripped before wrapping, so a wrapped
list item was indistinguishable from a new one and blank lines were dropped; configuration
option help lost its line breaks entirely.
#15043: Cache writes now emit
pytest.PytestCacheWarningwhen writing or closing the cache file fails, consistent with errors opening the file.#15052: Fixed possible off-by-one line numbers in
--fixturesand--fixtures-per-testoutput.#15145: With
console_output_styleset totimes, durations just below a unit boundary are now shown in the next unit (e.g.1.000msinstead of1000.0us).#15172: Assertion diffs now pretty-print the built-in
frozendict(Python 3.15+) across multiple lines, similarly to howdictis displayed.#4569:
-kno longer matches attributes that end up in a test function's__dict__without being meant as keywords, such as pytest's ownpytestmarkattribute and the bookkeeping left behind byfunctools.wrapsandfunctools.lru_cache.#6750: Fix fixture argument discovery for custom collectors that pass bound methods to
pytest.Function.#6997: XFAIL reports from teardown phases now include the phase in the short test
summary.
#7940: Fixed cleanup of temporary directories failing when subdirectories have their execute permission removed.
#8321: Fixed exception chains being duplicated in the traceback output when an exception in the chain (
__cause__/__context__) has no traceback of its own, and that exception is itself linked to further exceptions.#8914: Tests parametrized with higher-than-function scope are now reordered by
parameter value rather than by parameter index, so that items using equal
parameter values are grouped together and share a single fixture setup, even
when they are parametrized by separate
@pytest.mark.parametrize <pytest.mark.parametrize ref>calls. Previously, an expensive higher-scoped fixture could be set up and torn
down repeatedly for the same parameter value.
Unhashable parameter values (e.g. dicts) keep the previous index-based
grouping behavior.
As a consequence, the order in which parametrized tests run may change
(fixture setup/teardown counts can only decrease, not increase).
#9924: Fixed doctest option flags leaking between docstrings after a failure, skip, or expected failure.
Improved documentation
setup.pyusage withpyproject.tomlinwriting-plugins.--confcutdiraffects initialconftest.pydiscovery.pytest-flakefightersplugin to the list of flaky test plugins inplugins.rst.Added flaky test papers published on the Test FLARE project to the list of flaky papers in
plugins.rst.installation instructions <installation>andinstall-package-with-pipnow show how to install pytest into a virtual environment with venv and pip, uv or virtualenv, and explain theexternally-managed-environmenterror.pytest --help, the-kreference entry andselect-testsno longer describe-kas a Python expression matched against test names only;keyword expressionsnow shows marker names being matched as well. The examples that looked up a marker useNode.get_closest_marker <_pytest.nodes.Node.get_closest_marker>rather thanitem.keywords.session.itemsandsession.testscollectedexclude deselected tests in thepytest_collectiondocumentation.Packaging updates and notes for downstreams
#11680: Source archives of arbitrary commits -- the tarballs GitHub serves under
/archive/, not just the ones for tags -- can now be built into an sdist or awheel with a correct version. A
.git_archival.txtfile, expanded bygit archiveviaexport-subst, carries the describe output thatsetuptools-scmwould otherwise have to askgitfor.The archival file deliberately omits
ref-names. Recording it would make thecontent of an archive -- and therefore its checksum -- change whenever a branch
that points at the archived commit is moved afterwards, which is how the
equivalent tarballs of tagged releases became unstable for other projects.
Describe output alone depends only on the commit and on the tags reachable from
it.
The archival file is not shipped in the sdist -- an sdist carries its version
in its metadata, and the template is only ever expanded by
git archive.Building pytest from a git checkout now requires
setuptools-scm >= 10.1.#9236:
pluggy>=1.6is now required.Older pluggy versions stopped running the remaining hook wrapper teardowns once an
old-style
hookwrapperraised in its own teardown. The logging plugin's wrapper wasleft suspended, and when it was finalised later it detached the handler that
caplogof a subsequent test was recording through, so that test saw no records.Contributor-facing changes
DistFacadecompatibility class.Miscellaneous internal changes
FixtureRequest.node <pytest.FixtureRequest.node>is now explicitly annotated as returningAny, so strict pyright no longer reports it asUnknown. The return type stays deliberately broad, see13888.::-separated strings identifying collected items, e.g.path/to/test_file.py::TestClass::test_method[param]) are now represented internally by a single structured~_pytest.nodeid.NodeIddataclass instead of being repeatedly re-parsed as plain strings. The publicnodeid: strattribute on nodes and reports is unchanged and remains fully backward compatible for plugins.theskumar/python-dotenv (python-dotenv)
v1.2.4Compare Source
Fixed
dotenv getno longer exits with code 1 for empty string values (KEY=) by [@ShamikOfficial] in [#700]KEY= # comment) is now parsed as an empty string instead of the comment text by [@Noethix55555] in [#663]dotenv run --no-overridenow expands variable references with the same precedence asload_dotenv(override=False), so a value like${BASE}/suffixuses the existingBASEfrom the environment instead of the one from the.envfile by [@ROTl24] in [#698]v1.2.3Compare Source
Fixed
.envfile contents so the first variable is no longer silently lost when the file is saved with BOM (e.g. by some JetBrains IDEs on Windows) by [@h1whelan] in [#640]set_keynow escapes backslashes, so values containing them (Windows paths, regular expressions) survive a write/read round-trip. Quoted values ending in an escaped backslash are no longer mis-parsed as an escaped quote, which used to swallow the following lines by [@dchaudhari7177] in [#680]dotenv runnow prints a friendly error instead of a traceback when no command is given by [@bbc2] in [#606].envfiles so repeateddotenv_values/load_dotenvcalls no longer re-read the file by [@ReinerBRO] in [#638]Kludex/python-multipart (python-multipart)
v0.0.33Compare Source
FormParser#333.parse_options_headerwhen the header has parameters #287.Content-Transfer-Encodingdecoding before invoking field and file callbacks #336.UPLOAD_KEEP_FILENAMEorUPLOAD_KEEP_EXTENSIONSis set #343.strUPLOAD_DIRwhenUPLOAD_KEEP_FILENAMEis set #335.astral-sh/ruff (ruff)
v0.17.4Compare Source
Released on 2026-11-05.
Preview features
flake8-logging-format] Avoid behavior changes and syntax errors in fixes (G004) (#27419)security] Implementtarfile-ignore-zeros-true(#28680)Bug fixes
flake8-simplify] Report collapsibleelifclauses after a multi-statementifbranch (SIM102) (#29814)Rule changes
Performance
pyflakes] Defer value comparisons for repeated dictionary keys (F601,F602) (#29739)CLI
NO_COLORprecedence in Ruff and ty diagnostics (#29898)CLICOLOR=0in Ruff and ty diagnostics (#29910)Documentation
versionandsrcdefaults forruff-action(#29880)flake8-bugbear] MakeB014example error out-of-the-box (#29941)flake8-no-pep420] Document preview behavior (INP001) (#29764)isort] ExpandI001documentation (#29843)Other changes
__debug__write check to attribute targets (#27908)Contributors
v0.17.3Compare Source
Released on 2026-10-26.
Preview features
suspicious] Addshared-clock-mock(#29074)Bug fixes
pyupgrade] Avoid incorrect conversions to f-strings (UP032) (#25485)Performance
ruff format(#29631)Documentation
flake8-bandit] Describe what the rule actually checks forhttpx(S113) (#27936)Contributors
v0.17.2Compare Source
Released on 2026-10-22.
Preview features
complexity] Addunnecessary-regular-expression-compile(#25533)Bug fixes
UP040,UP046,UP047,RUF053) (#28362)pydocstyle] Treat backslash-escaped*argsand**kwargsas documented (D417) (#29536)pylint] Avoid false positive on starred elements (PLE0643) (#29483)pylint] Mark fixes unsafe when deleting comments (PLR5501,PLW0120) (#29414)pyupgrade] Mark fix unsafe when deleting comments (UP035) (#29413)Performance
Configuration
pylint] Add a configurable allowlist formagic-value-comparison(PLR2004) (#18961)Documentation
pyupgrade] Remove mentions of variance related to fix safety (UP040,UP047) (#29592)Contributors
v0.17.1Compare Source
Released on 2026-10-15.
Preview features
pycodestyle] Don't treatNone,True, orFalseas keywords (E271-E274) (#29322)Bug fixes
flake8-pyi] Detect legacy positional-only parameters in__new__methods (PYI063) (#28502)flake8-pyi] Fix false positives on quoted annotations (PYI036) (#29274)perflint] Avoid invalid comprehension suggestions (PERF401,PERF403) (#29366)Rule changes
collectionsandweakrefcontainers as mutable (#29213)Documentation
flake8-import-conventions] Update default and examplealiases(#29333)Contributors
v0.17.0Compare Source
Released on 2026-10-09.
The executables in our macOS and Windows release archives and
ruffwheels are now code-signed.macOS executables are signed with an Apple Developer ID certificate and notarized by Apple. Windows
executables have timestamped Authenticode signatures from Azure Artifact Signing. This enables
verification of the release publisher and binary integrity, supports publisher-based allowlisting,
and should reduce security warnings and antivirus false positives.
Breaking changes
Update the default and latest Python versions for 3.15 (#28792)
Ruff now defaults to Python 3.11 instead of 3.10 when no Python version is configured through
target-versionorrequires-python. Whenchecking for syntax errors without a configured Python version, Ruff now defaults to Python 3.15
instead of 3.14.
Update the default rule set (#28786)
Several of the
flake8-datetimezrules(
DTZ001,DTZ005,DTZ006,DTZ007,DTZ011,DTZ012, andDTZ901) are no longer enabled by default,while
undefined-local-with-nested-import-star-usage(
F406), which corresponds to a syntax error, is now enabled by default.Update Rust crate quick-junit to 0.8.0 (#27295)
JUnit output now uses a
skippedattribute instead ofdisabledon<testsuite>elements andincludes a
skippedattribute on the root<testsuites>element.[
flake8-import-conventions] Adddatetime as dtas a conventional alias (ICN001) (#28790)Support Unicode dummy variable names (#28722)
The default
lint.dummy-variable-rgxnow recognizes underscore-prefixed Unicode names, such as
_次, as dummy variables.Update to Unicode 17 (#21229, #28784)
Ruff now uses Unicode 17 data for identifier normalization and named character escapes (
"\N{...}").Always show unsafe and display-only fixes in the CLI (#27810)
The default
fulloutput format now shows unsafe fixes and suggestions requiring manual review,regardless of the
unsafe-fixessetting.Actually applying unsafe fixes still requires explicit opt-in.
Remove the Python dependency from conda-forge builds (conda-forge/ruff-feedstock#361)
The conda-forge build no longer depends on Python, now supports linux-riscv64, win-arm64, and
linux-ppc64le platforms, and now includes shell completions. However, no longer depending on
Python means that
python -m ruffandimport ruffwill no longer work. Useruffdirectly fromPATHinstead. PyPI installations and those from the standalone installer are unaffected.Remove support for ruff-lsp (#28750)
Support for
ruff-lsp, the legacy Python language server deprecated in Ruffv0.9.5, has been removed. The Ruff VS Code
extension now always uses the native language server;
ruff.nativeServeris deprecated andignored. See the migration guide.
Stabilization
The following rules have been stabilized and are no longer in preview:
lazy-import-mismatch(TID254)lazy-import-immediately-resolved(
TID255)os-path-commonprefix(RUF071)The following behaviors have been stabilized:
unsorted-imports(I001),line-too-long(E501), anddoc-line-too-long(W505) nowconsistently ignore trailing pragma comments when computing line length. This resolved several
bugs involving interactions between these rules
(#27313) but may also cause existing imports to be
reformatted and was thus classified as a breaking change.
Preview features
flake8-bugbear] Report the method name and a more precise range (B005) (#27050)refurb] Mark fix unsafe and move tosuspicious(FURB152) (#28405)ruff] Allow docstrings in strict mode (RUF067) (#28679)Bug fixes
flake8-builtins] Expand checks in class scopes (A001) (#29076)flake8-self] Allow private access onobject.__new__(cls)instances (SLF001) (#29001)flake8-tidy-imports] Skiplazy-import-mismatchin stubs (TID254) (#29095)flake8-type-checking] Add the notion of runtime-ambiguous references (#26508)flake8-type-checking] Never flag annotations in function scopes (#29183)pyflakes] Mark the fix as unsafe when it creates a docstring (F541) (#28258)pylint] Preserve trailing comments inuseless-returnfix (PLR1711) (#29180)ruff] Avoid false positive whenpytest.raisesis used in awithstatement (RUF061) (#28186)Rule changes
pyupgrade] Suggesttyping.TypeFormon Python 3.15 (UP035) (#29084)Contributors
Kludex/starlette (starlette)
v1.10.0: Version 1.10.0Compare Source
📝 Leaner URL-encoded form parsing
Starlette 1.10.0 parses URL-encoded forms with less memory and CPU, no longer drops the last field when a stream ends early, and lets you compare
FormDatathat holds several uploads under one key.FormParserbuilds each field in the parser callbacks instead of queueing every event and replaying it. On CodSpeed, 1000 fields went from 42.8 ms to 34.9 ms and from 63.5 KB to 54.5 KB, and themax_fieldslimit case went from 6,880 B to 864 B.b""no longer lose their last field.FormDatawith repeated uploads can be compared (#3619). Comparing twoFormDataobjects with severalUploadFilevalues under the same key used to raiseTypeError.⚠️ Behaviour changes
QueryParams("a=1&a=2")andQueryParams("a=2&a=1")are no longer equal, matching whatgetlist()returns.starlette.formparsers.FormMessageis deprecated (#3724). The parser no longer uses it, andFormParser.messagesis gone. CallingFormMessage(...)emits aStarletteDeprecationWarning.🧭 Routing and URL generation
url_path_for("route", category="{id}", id="42")on/{category}/{id}now returns/{id}/42instead of/42/42. Substitution also got faster, from 549 ns to 520 ns with two parameters and from 1,078 ns to 779 ns with five.Matchmembers are compared by identity, cutting up to 4.9% of the instructions per dispatch in the routing benchmarks.WSGIMiddlewarestripsroot_pathonly at a path segment boundary (#3718). A request to/applicationunder aroot_pathof/appno longer becomes/lication.FloatConvertorreports NaN and infinity correctly (#3716). NaN was reported as a negative float.🛡️ Middleware, responses and config
HTTPException, no longer pins per-request objects in memory.http.response.startwithoutheaders(#3721).BaseHTTPMiddleware,GZipMiddlewareandSessionMiddlewaredefault the omitted field to an empty list instead of raisingKeyError.FileResponseignoresRangeon methods other thanGETandHEAD(#3679). APOSTendpoint that returns a file now sends the full file, as RFC 9110 requires.Configstrips one pair of matching outer quotes from env file values (#3720).KEY='say "hi"'now reads assay "hi"instead ofsay "hi.Full changelog: 1.9.0...1.10.0
v1.9.0: Version 1.9.0Compare Source
This release streams
TestClientresponse bodies, soclient.stream()returns before the application finishes. It also escapes path delimiters when building URLs, matches host names case-insensitively, and keeps every existingVaryheader.Changed
TestClientresponse bodies, soclient.stream()returns before the application finishes. Closing an unfinished streamed response now cancels the application #3491.Fixed
?,#,\and a leading//in the request path when buildingrequest.url, and\and a leading//inurl_path_for#3671.Content-Lengthwhen the response body is a typed or multidimensionalmemoryview#3622.HEADin theAllowheader ofHTTPEndpoint405 responses whenGETis defined #3585.Hostrouting andTrustedHostMiddleware#3542.Varyheader inMutableHeaders.add_vary_header(), used byGZipMiddlewareandSessionMiddleware#3675.Full changelog: 1.8.0...1.9.0
jannikmi/timezonefinder (timezonefinder)
v8.3.0Compare Source
New features:
TimezoneFinder().data_version(andTimezoneFinderL().data_version) return the timezone-boundary-builder release the packaged data was built from, read from adata_version.txtstamp thatscripts/file_converter.pywrites into the data directory it generates and that ships in the wheel. Previously an installedtimezonefindercould not state it at all: the release tag lived only in a repo-root file that is not packaged. Which release a parse is stamped with comes from the input's filename (combined-with-oceans-2026c.json, whichupdate_data.shnow produces), or fromscripts/file_converter.py --data-versionfor an input that cannot carry it; your own GeoJSON is stamped"unknown", and an unpacked release archive that lost its tag is refused rather than compiled into data that could never say where it came from.timezonefinder.__version__is now exposed as well, read from the installed distribution metadata. Solves issue #498.--stdinstreaming mode: it reads delimited rows from standard input and writes each back out with atimezonecolumn appended, building the finder once instead of paying full initialisation per coordinate. Which columns hold the coordinates is read off the header by name, or stated with--lng-col/--lat-col, and never inferred from their position - a swapped pair is still a valid coordinate for any longitude between -90 and 90, so guessing would answer with a real but wrong timezone instead of failing. Every input row produces exactly one output row, and a row that cannot be used warns on stderr and makes the run exit non-zero rather than ending the stream. Whether the first row is a header is worked out from the row, or stated with--header/--no-header. New flags-d/--delimiterand--in-memoryapply to the whole stream. See theusage documentation <https://timezonefinder.readthedocs.io/en/latest/1_usage.html#looking-up-many-coordinates-at-once>. Solves issue #504. Thanks toweed33834 <https://github.com/weed33834>for the PR #516.Changed:
coordinates.binfiles - all x values followed by all y values per polygon, instead of interleaved. The point in polygon test scans a single axis per iteration, so contiguous per-axis blocks halve the cache lines it touches: ~1.6x faster on a median polygon and ~2.5x faster on the largest ones via the C extension, 14-25% faster via Numba. The bundled data was regenerated accordingly, and the layout is described in thedata format documentation <https://timezonefinder.readthedocs.io/en/latest/data_format.html>__.TimezoneFinderraises aValueErrornaming the offending file when either does not match - previously such a directory was read without complaint and produced wrong timezones. Forcoordinates.binthe version records how the coordinates are encoded and which polygons the file holds. The hybrid shortcut binaries get an identifier that differs per zone id width, because the uint8 and uint16 schemas differ only in the width of a zone id and each parses cleanly as the other; the width is now read from the buffer instead of being guessed from the file name, so a renamed or mispaired shortcut file fails loudly rather than returning wrong zones. If you compile your own data and pointbin_file_locationat it, regenerate it once withscripts/file_converter.py, since the coordinate layout, the hole storage, the shortcut container and the file names all changed in this release. The markers track what a file holds rather than the package version, so this is not a per-release obligation. Solves issue #458.TimezoneFinder/TimezoneFinderLinstance. They are now disjoint read-only slices of a single compact array, cutting the shortcut mapping's footprint from ~7.4 MB to ~4.7 MB per instance, and every finder's resident set by ~2 MB, at unchanged initialisation time - which matters most for concurrent workloads, where the recommended one-instance-per-thread pattern multiplied the waste.holes/poly_ref.npyrecords per hole which boundary polygon to read instead. Hole data drops from ~2.0 MiB to ~0.16 MiB, andin_memory=Truesaves the same amount of RAM, since those holes now resolve into the boundary arrays rather than materialising a second copy. Matching is exact - rings are compared as integer coordinates in a canonical form, with bounding boxes used only to narrow the search - so every timezone lookup returns what it did before. One visible consequence:get_geometry()may hand back a deduplicated hole ring starting at a different vertex or winding the other way than it used to, tracing the same closed path. The encoding is described in thedata format documentation <https://timezonefinder.readthedocs.io/en/latest/data_format.html>__.timezonefinder-data.pip install timezonefinderis unchanged - it is a hard dependency and is installed automatically - but the dataset can now be pinned on its own (pip install timezonefinder "timezonefinder-data==1.2026.3"), where previously holding a dataset meant pinning an oldtimezonefinderand forfeiting every code fix since. Every release used to carry the whole ~65 MB dataset in three platform wheels plus an sdist to distinguish a few kilobytes of compiled code, which had already exhausted the PyPI project storage quota once. A data update is consequently no longer atimezonefinderrelease at all: it publishestimezonefinder-dataunder its own tag namespace and is recorded in that package's README rather than here. Its version reads<format>.<year>.<letter>-1.2026.3is data format generation 1 built from timezone-boundary-builder2026c- andtimezonefinderrequirestimezonefinder-data>=…,<2: no ceiling on the data axis, so a dataset update needs no code release, and a hard one on the format axis, so code paired with data it cannot read fails when resolving rather than at the first lookup.DATA_LICENSEmoves with the database it covers and now ships inside the data wheel, and a compiled data directory additionally carries aschemas/copy of the FlatBuffers definitions its binaries were written by, so it can be read back without the package that wrote it. Solves the first part of issue #446..binrather than.fbs:boundaries/coordinates.bin,holes/coordinates.binandhybrid_shortcuts_uint16.bin..fbsis the FlatBuffers schema extension, and the data directory now ships actual schemas next to the buffers, so one extension was naming two unrelated kinds of file. Each buffer already states what it is through the file identifier in its first bytes, which is what a rename or a mispaired copy cannot forge - the name never carried that meaning. The bytes are unchanged.Bug fixes:
BufferError: cannot close exported pointers existraised during resource cleanup in file mode (in_memory=False). A coordinate array obtained fromcoords_of()is a zero-copy view onto the memory-mapped file, andmmap.close()refuses to unmap while one is alive, so an array outliving itsTimezoneFinderraised on cleanup. The mapping now stays valid instead of leaving the views dangling, andFileCoordAccessor.cleanup()releases its own references so the deferred close happens as soon as the last view is dropped. The accessor must not be used aftercleanup().Documentation:
memory report <https://timezonefinder.readthedocs.io/en/latest/benchmark_results_memory.html>__, separating what a configuration allocates (tracemalloc) from what it makes resident (RSS, which additionally counts memory-mapped pages). The distinction is the point: the default mode maps the coordinate data instead of reading it, so it allocates an order of magnitude less than the in-memory mode, and only the pages a lookup actually touches become resident. This replaces documentation claiming a 40MB process ceiling and a 41MB data directory, both long out of date.README.rstand thedocumentation landing page <https://timezonefinder.readthedocs.io/en/latest/>__ - so both state what the package is and how it works instead of only what it is called. The README opens with the project banner and a one-sentence statement of what the package is for, then the badges, then the quick guide - and adds three short sections that were missing entirely: How it works (the lookup pipeline and the no-simplification trade-off), Performance (a concrete throughput figure with its configuration named, the three point-in-polygon backends and the pure-Python fallback), and Engineering notes linking the architecture, data format and benchmarking methodology pages. The maintainers-wanted notice moves from the first heading after the intro into a newContributingsection at the bottom, which also linksCONTRIBUTING.mdfor the first time. The badge block is corrected along the way: thecode style: blackbadge named a formatter this project has never used and is replaced byruff, and a supported-Python-versions badge was added. The banner is referenced by absolute URL, since PyPI serves the long description without the repository and adocs/…path renders as a broken image there. The landing page gains the same How it works summary, the no-simplification trade-off and the ocean-zone consequence fortimezone_at(), and its flat seventeen-entry table of contents is grouped into Using it, Design, Performance and Project, so the sidebar says what kind of project this is rather than listing pages in the order they were written.package comparison <https://timezonefinder.readthedocs.io/en/latest/alternatives.html>__ page. It now states its position in prose before the first table - border correctness is what this package optimises for, speed is the constraint that work happens under - and says plainly whentzfpyis the better choice. Every quantitative cell names what it measures and links its source, and the speed row is deliberately qualitative on both sides, with a note explaining that the two packages have never been benchmarked under one harness. The decision table drops the rows on which the two packages do not differ.Architecture <https://timezonefinder.readthedocs.io/en/latest/architecture.html>__ describes the lookup pipeline, the three point-in-polygon backends and the memory modes, and states the ceilings this package deliberately does not exceed - unsimplified geometry, ~1 cm coordinate resolution, no general-purpose spatial code. It also documents how the package is built and shipped, which was previously described nowhere outside the workflow YAML: why one abi3 wheel per target replaces one wheel per Python version and whatabi3auditis guarding, why three libc targets are built, why the end-to-end job installs the built wheel and asserts the C extension loaded rather than merely importing the package, and why a tag pushed from outsidemasteraborts the release. The testing section gained the property-based suite and the reason the tox matrix is a matrix - the acceleration paths are bound at import time, so a passing run describes one configuration only. Both sections are linked from the README's Engineering notes.Benchmarking Methodology <https://timezonefinder.readthedocs.io/en/latest/benchmarking_methodology.html>__ documents how the published numbers are produced and what they can and cannot tell you:ubuntu-latestpins the runner image and not the CPU, which is why a pull request is measured against its own merge base on the same runner and why every alert threshold is derived from measured noise. It was previously addressed only to contributors, in the second half ofCONTRIBUTING.md, which now keeps the operational instructions and links to it.data format documentation <https://timezonefinder.readthedocs.io/en/latest/data_format.html>__ is no longer asserted to "offer a good balance" but reports the study behind it (prototypes/single_resolution_bench.py): resolution 3 keeps the hybrid index at a small fraction of the packaged polygon data, while resolution 4 would exceed 10 % of it for gains that do not justify the increase.the data report <https://timezonefinder.readthedocs.io/en/latest/data_report.html>__ or the relevantbenchmark report <https://timezonefinder.readthedocs.io/en/latest/7_performance.html>__, which are regenerated from the packaged data and are always current.performance page <https://timezonefinder.readthedocs.io/en/latest/7_performance.html>__ now opens with the four benchmark reports and the trend chart instead of a bullet list of adjectives about the binary format, and its C extension and Numba sections are cut to what a user does - which call reports the active backend - with the explanation left to the architecture page that already carried a more precise version of it. Getting started lists the four runtime dependencies and what each is for, where it previously said to consultpyproject.toml, which remains linked as the authoritative source for version ranges. The use case pages carry runnable snippets for building an awaredatetimeand reading a UTC offset, with theexamples/scripts as the follow-up rather than the whole answer; the snippets use the standard library'szoneinfo, so neither needs an optional dependency.data report <https://timezonefinder.readthedocs.io/en/latest/data_report.html>__ no longer report three quarters of all H3 cells as holding0polygons, which is impossible for data whose ocean zones cover the globe. Those cells are covered by a single timezone and store its id directly, so a lookup there needs no point-in-polygon test at all - the column is now Polygons to test and the row reads none (unique zone). The tables are introduced by a sentence on what they measure, including why no cell ever needs exactly one test.README.rstand theusage documentation <https://timezonefinder.readthedocs.io/en/latest/1_usage.html>__ now show the result the packaged data actually returns. Every snippet queries the same Berlin coordinates and annotated the answer as'Europe/Paris', which is the value from the reducedtimezones-nowdataset, whereEurope/Berlinis merged intoEurope/Paris- not from the full dataset the package ships by default. All eleven annotations now read'Europe/Berlin', verified against the packaged data for each oftimezone_at(),timezone_at_land(),certain_timezone_at(),unique_timezone_at()andTimezoneFinderL, and theget_geometry()call in the opening example asks for that same zone instead of a different one.tests/test_documented_contracts.pynow re-runs each of those documented lookups, so a data update that moves the example coordinate's zone fails there rather than leaving every snippet on both pages quietly wrong again.Internal:
timezonefinderunless a compatibletimezonefinder-dataalready exists on PyPI. The two distributions release independently, and on a data format change the order is fixed - data first, then the code requiring it - because a code wheel whose declared data version does not exist yet is uninstallable for everyone until it does, and the version number cannot be reused to fix it. The check reads the requirement out of the built wheel rather than out ofpyproject.toml, and asks the index the same question a user's resolver will, so a yanked release does not count as one that satisfies it. It runs before the GitHub Release, which is the first step of the release that cannot be taken back..github/pull_request_template.md) prompting for the change, its motivation and the checks that were run.update_data.shresolves the timezone-boundary-builder release tag before downloading and fetches that release's asset, instead of fetchingreleases/latest/download/and separately asking the API whatlatestwas - two independent questions that a release landing between them answered differently, attributing one release's data to the other. The tag now names the downloaded archive and the GeoJSON as well, so a leftover file from another release or another dataset variant cannot satisfy the "already downloaded" checks and be parsed in place of what was asked for.TimezoneFinder.timezone_atdocuments the optimisation it actually performs: once no other zone can be matched the last remaining zone is returned without a point in polygon test, which is always correct against the packaged data - the ocean zones cover the globe, so every point lies within one of the candidate polygons - but not against custom data that leaves areas uncovered, where a point inside none of the candidates is still attributed to that zone andcertain_timezone_atis the method that tests every candidate. Three tests were named after something other than what they do:test_rectify_coords_valid/_invalidwere named for arectify_coordsthat exists nowhere in the package and both callvalidate_coordinates, and the first was subsumed entirely bytest_validate_coordinates_accepts_finite_values, which covers all four of its distinct corners and additionally asserts the return value where the older test asserted only "does not raise"; andtest_single_element_arrays_should_not_occurasserted that they do occur (assert single_element_count == 2) under a triple-quoted string placed after the first statement, making it a discarded expression rather than a docstring - so it reached neither--collect-onlynor a failure report, which is where the contradicting name was the only thing a reader saw. A stale comment duplicated across the last two lines oftests/main_test.py, reading as a to-do for somethingTestTimezonefinderClassTestMEMalready does, is gone.DATA_VERSIONfile tracking which timezone-boundary-builder release the packaged data was generated from, written automatically by the data update script after a successful parse. Thanks toLucas Hemkemeier <https://github.com/hemkdev>__ for the PR #429.DATA_VERSIONagainst the latest timezone-boundary-builder release, regenerates the data and opens a ready-to-review update PR, which is merged and released automatically once its CI passes - the version tag is pushed with a GitHub App token, since the default one would not trigger the release pipeline. The tag lives in its owndata-v*namespace, which the code release pipeline excludes at its trigger and again on the job that creates the GitHub Release, and the data stream publishes by PyPI Trusted Publishing from its own deployment environment rather than with a shared token. It refuses to release when the squash it produced did not land on themasterit checked, so the tag names a tree that was actually built. Failed CI takes the same manual-attention path and falls back to the previous notification issue. Each cause labels the PRautomation-failedand leaves one comment naming that cause and linking the run, deduplicated per cause so re-running CI neither repeats a notice nor hides a second one; a failure past the merge is a cause of its own, since that leaves master carrying the update with no tag pushed and only a hand-pushed tag still releases it. The manual release path drops the stop condition it carried for an out-of-orderCHANGELOG.rst: the automation can no longer produce one, and the test suite asserts the committed file's section order if anything else does (issues #273, #167 and #510). Thanks toLucas Hemkemeier <https://github.com/hemkdev>__ for the PRs #434 and #436, and toNice6042 <https://github.com/Nice6042>__ for the PR #518.update_data.sh(renamed fromparse_data.sh) is CI-ready: interactive prompts replaced by flags (--dataset=full|same-since-now,--with-oceans,--rm-tmp), the release note for a data update written automatically into the data package's README, no redundanttoxrun, and amake reportsat the end so the benchmark and data reports cannot go stale relative to the data an update PR ships. A standalonemake parse/make testparsestill needs a manualmake reports(issues #167 and #510). Thanks toLucas Hemkemeier <https://github.com/hemkdev>__ for the PRs #432 and #434.hypothesis) for coordinate validation (solves issue #143). Thanks toLu Yicheng <https://github.com/01luyicheng>__ for the PRs #431 and #433.timeittiming inscripts/check_speed_*.pywithpytest-benchmarksuites underbenchmarks/, excluded frommake test/make testallviatestpaths. Both they and the memory harness run over deterministic committed fixtures (tests/fixtures/benchmarks/), so two runs of the same commit execute the exact same workload; the loader rejects fixtures that no longer match the checkout. Measurement and rendering are decoupled, sodocs/benchmark_results_*.rstcan be regenerated from a stored JSON without re-measuring. Run viamake speedtest,make benchmarks,make memoryormake reports.scripts/measure_memory.py,make memory) rather than bypytest-benchmark, which times code and would have its timings distorted by allocation tracking. It emits pytest-benchmark-shaped JSON, so the existing normalisation, noise and comparison tooling works on it unchanged given a--metric.tests/test_memory_footprint.pyfails if a mode's allocation leaves its order of magnitude - the regression that would makein_memory=Falsestop being the low-memory option.build.yml: the tracked core subset and the memory harness run on every pull request and every push tomaster, publishingtrend charts <https://jannikmi.github.io/timezonefinder/dev/bench/>__ togh-pagesand posting a same-runner base/head comparison on the pull request. The comparison reports added or removed benchmark IDs without trying to ratio unmatched measurements, which lets the trusted default-branch consumer remain compatible while the suite evolves. A pull request is measured against its own merge base in the same job rather than against a stored baseline, becauseruns-on: ubuntu-latestpins the runner image and not the CPU. The measurement design, the tracked estimator and every alert threshold are documented in the newbenchmarking methodology <https://timezonefinder.readthedocs.io/en/latest/benchmarking_methodology.html>__ page. The measuring job holds no write permissions and no secrets, so branch and fork pull requests behave identically; the comment is posted by a separate, privileged workflow viaworkflow_run.tests/test_benchmark_names.pyandtests/test_memory_metric_names.pypin the node ids and metric names that join a measurement to its chart history, so a rename fails loudly instead of starting an empty chart beside the orphaned old one;tests/test_benchmark_workflows.pyasserts that the constants duplicated across the two workflows agree, where a one-sided edit previously had no failure mode at all, and that the cross-machine trend chart cannot creep back into the pull request comparison; and every generated report page states the inputs it describes -docs/benchmark_results_*.rstthe fixture and timezone data versions they were measured against,docs/data_report.rstthe timezone data version its figures were derived from. Both stamps are covered by tests: one renders each report and fails if a renderer stops emitting it, another checks the committed pages against the current fixture metadata andDATA_VERSION, so regenerating fixtures or updating the data without re-rendering fails loudly instead of leaving a page whose numbers are all plausible and all stale.write_jsonsorts keys the waypretty-format-jsondoes, and neitherscripts/reporting.pynorBenchmarkReporteremits trailing whitespace on empty cells or a trailing blank line. Previously everymake parse/make reportsleft its outputs looking modified until the hooks had run, which masked whether a regeneration had actually changed anything.make flatbufno longer overwrites hand-maintained__init__.pyfiles.flatcderives its output path from the schema namespace and writes an empty__init__.pyat every level of it, so generating in place wiped the__all__intimezonefinder/__init__.py- the whole public API. The target now generates into a scratch tree, copies back only the generated packages, and runs the formatters on the result so a regeneration diff shows the codegen change rather than formatting churn.flatc-generated bindings.ignore_errorspreviously covered roughly 800 lines of hand-written code as well, where a blatantly wrong return type still reported "Success"; they all pass once the exemption is lifted, bar two genuine findings now fixed.tests/test_mypy_config.pykeeps the list restricted to generated code, so silencing a module is a reviewed decision rather than a one-line edit.SHORTCUT_SCHEMASintimezonefinder/flatbuf/io/hybrid_shortcuts.py) instead of dispatching on the zone id width in three places, each keyed differently. OneShortcutSchemaper width owns the width, the file name, theuintNmarker and the maximum zone id, which were previously written down across five places with nothing tying them together. Verified behaviour-preserving down to the bytes: re-writing the shipped shortcut binary produces a byte-identical file.build/liband never prunes it, so a file renamed in the source tree keeps being zipped into every later wheel built from that checkout, which is how a 63 MBcoordinates.fbswas still shipping next to thecoordinates.binthat replaced it and doubling the wheel whose size is the reason the distribution was split out. The wheel builders clear that directory first, so a local build matches the fresh checkout CI builds from, and the code sdist's checks cover its grafted test fixtures again.scripts/file_converter.pyis a supported use case, holes that are ordinary interior rings rather than enclaves are stored inline and answer correctly, and the converter only reports the ratio rather than refusing to compile.prototypes/hole_boundary_redundancy.pyis the study behind the threshold: it reads the upstream GeoJSON, so re-running it against a new release re-verifies the assumption rather than restating it.prototypes/hole_removal_impact.pyis the study behind keeping the unmatched holes stored inline rather than dropping them, which is the obvious next step and does not work: dropping holes and re-running the lookups changes answers, wrongly, because being covered by another zone only puts that zone among the shortcut candidates and says nothing about it being tested first (issue #513).__slots__entries were declared but assigned by nothing, which silently re-permitted the very attributes__slots__is there to forbid - assigning those names now raisesAttributeError, andtest_declared_slots_are_assignedkeeps the list honest.get_corrected_hex_boundariesexists once again. An earlier refactor left two verbatim copies of the antimeridian and pole clipping rules with nothing keeping them in sync; the copy without callers is deleted, and the survivor is now covered bytests/hex_utils_test.py- it previously had no direct tests at all.scripts/configs.pyno longer declaresMAX_LAT/MAX_LNGas a second pair of names fortimezonefinder.configs's constants.prototypes/has aREADME.mdsaying what the three scripts there are: exploratory studies behind committed design decisions, run by hand, outside the package and the test suite. One of them is the measurement that chose H3 resolution 3 - the central algorithmic parameter of the package, already cited from the data format page - and another is the evidence for not building a hierarchical index.MANIFEST.innow excludes the whole directory from the source distribution rather than only its*.pyfiles.plans/is git-ignored alongsidetmp/and.venv/: implementation plans written while working on a change are local scratch, and leaving the directory untracked-but-unignored made it noise in everygit statusand a candidate for an over-broadgit add.tests/auxiliaries.py'srun_commandassembled the child's stdout and stderr into a message and then raised a freshCalledProcessErrorthat never used it, withfrom Nonediscarding the original too, so a packaging failure undermake testintreported an exit code and nothing about the cause; it now echoes the captured streams and re-raises the original exception with its traceback intact.scripts/reporting.pypasses the coordinate file paths intoget_polygon_collection, whose optionalfile_pathexists precisely so an incompatible-layoutValueErrorcan say which of the two files was stale -make reportsagainst an outdated data directory previously could not.Boundaries.overlapsnames the type it rejected instead of raising a bareTypeError, and theRuntimeErrorfor missingoriginal_polygonsnames the polygon and resolution it was computing. The two re-raises ruff flags underB904now sayfrom Noneexplicitly, so a deliberately dropped exception chain is distinguishable from a forgotten one, andtimezonefinder/command_line.pydropsFileNotFoundErrorfrom anexcepttuple that already caught its base classOSError.tests/test_error_diagnostics.pypins what each of these messages must contain.mainredirected stdout to amkstempfile for the duration of the lookup and then, in verbose mode, reopened it to read back a string it still held in a local variable - nothing inside the redirected block ever wrote to stdout, since the lookup functions return their result rather than printing it. The context manager, the read-back, its warning path and the file cleanup are gone, and the lookup function is now resolved once per invocation instead of twice, so-f 3/-f 4under-vno longer construct a secondTimezoneFinderLand reload its shortcut data just to read a function name. Output is unchanged character for character, across every function id in both modes.tests/cli_test.pygains the coverage that makes that checkable - verbose mode, the empty line printed when no timezone is found, and the rejected function id had none - and asserts the printed name verbatim instead of passing it throughrstrip("\n\x1b[0m"), which strips a set of characters rather than a suffix and so truncates 12 of the packaged zone names (Europe/Amsterdam->Europe/Amsterda).AbstractTimezoneFinder.__init__calledin_memoryinert and "kept for API compatibility" when it is what selects memory-mapped against in-memory coordinate access - the claimhelp(TimezoneFinder)surfaces, and the opposite of what the usage docs say; bothget_geometrydocstrings pointed at atimezone_names.jsonthat does not exist under that name;read_zone_namespromised an empty list where it raisesFileNotFoundError, and illustrated itself with a hardcoded zone count that the packaged data had since outgrown; andzone_id_of/zone_name_from_ideach advertised an exception type they convert away, sending callers to write handlers that can never fire. Five further:param:/Args:entries inscripts/andtests/documented arguments that were removed along with the parallel shortcut compilation they belonged to.tests/test_documented_contracts.pypins the exception types and the coordinate access mode, so those promises now rest on something besides prose.pytest.raisesblocks intests/main_test.py, and execution leaves such a block at the first statement to raise - so one out-of-range coordinate, one positional call shape and one rejectedget_geometryinput were verified while the remaining fifteen were unreachable. Each is a test case of its own now: every coordinate just outside the WGS84 range, every positional call shape of every keyword-only lookup method, and the unknown-zone-name, past-the-end and negative zone id rejections ofget_geometry. The__del__cleanup test binds its exception per iteration rather than closing over the loop variable, which decided what a garbage-collected instance would raise long after the loop had moved on. On the benchmark side,pip_inputs_by_stratumvalidated only the strata the fixture happened to contain, so one missing from it altogether passed and surfaced later as a bareKeyErrorinside a benchmark, and the points and their labels were paired from two files with a non-strictzipthat truncates silently. That grouping now lives intests/auxiliaries.pyasgroup_pip_inputs_by_stratum, checks against the declaredPIP_STRATA- which the generator no longer keeps a second copy of - and has tests for each way the two fixture files can disagree.uv sync --all-groups) makes it - so the C extension was reached only by direct-kernel tests on hand-built arrays, and everything about how real polygon buffers arrive at it, including the read-only memory-mapped views, was first exercised in CI's non-numba tox environments: the configuration a plainpip install timezonefinderproduces.tests/test_acceleration_paths.pynow rebindsutils.inside_polygonand drives the full lookup stack through both implementations, asserting that they agree across the real boundary data, that the C path returns the known-correct answers, and that the point-in-polygon stage was reached at all rather than short-circuited by the shortcut layer (issue #482).tests/test_package_contents.pyno longer names files that do not exist. It asserts that nothing in the built sdist and wheel matches a list of unwanted paths, which passes just as readily when a pattern matches nothing at all:.githublacked the trailing slash that directory patterns need,Agents.*stopped matching when the file was renamed toAGENTS.md, andreadthedocs.yamlnever matchedreadthedocs.yml- so the CI configuration and both of those files were unguarded while the suite stayed green. The patterns are corrected, the provider stubs,contributing/,.agents/,.claude/and.cursor/are covered to match whatMANIFEST.inexcludes, andtest_every_unwanted_pattern_matches_a_project_filenow fails on any hand-written pattern that matches no path in the checkout, so the next rename cannot silently disarm one. It carries theunitmarker rather than the module's former blanketintegrationmark, since it needs no build: a mistyped pattern surfaces inmake test..gitignorere-include lines (!…) are also no longer read as exclusions, which had produced one more parametrised case that could never fail. The converse direction is checked too:test_every_manifest_exclusion_is_guardedparses theexclude/recursive-exclude/prune/global-excludedirectives out ofMANIFEST.inand fails when one of them keeps a path out of the build that no pattern here names - previously such a line was enforced by the build and verified by nothing, so deleting it would have shipped the file with the suite still green. The two lists are hand-maintained statements of one intent and had drifted before, in both directions. Thearchitecture page <https://timezonefinder.readthedocs.io/en/latest/architecture.html>__ describes the guard from both sides: among the tests that exist to give an invariant a failure mode, and under How it ships as the check on what the built artifacts actually contain.uv buildwas invoked without--python, so it targeted the newest interpreter on the machine, whiletests/test_integration.pycreates its throwaway venv fromsys.executable: on a checkout whose.venvis older than the newest installed Python,make testintproduced acp314wheel and failed with pip's "not a supported wheel on this platform". Every tox environment offers a single interpreter, so the two agreed by accident in CI and the mismatch only ever hit developer machines, where the workaround was to pinUV_PYTHON.test_build_commands_pin_the_running_interpreterkeeps the pin in place; it needs no build, so it fails inmake testrather than waiting on a CI environment that cannot reproduce the mismatch.np.seterrand the warning filters are process-global, andtest_overflow(tests/main_test.py) plustest_inside_polygon(tests/utils_test.py, six parametrisations) each set them and never restored them - so every later test in the same process ran withunderpromoted fromignoretowarn, and which of the two modules pytest collected first decided the state the other ran under. The filters were undone only incidentally, by pytest's per-testcatch_warnings(), not by the tests themselves.benchmarks/conftest.pyalready had the correct pattern; it now lives intests/auxiliaries.pyas thestrict_numpy_errorscontext manager plus a thinstrict_numpy_warningsfixture, re-exported through the conftest of each suite, and both call sites request it. The context manager form is what makes the restore directly testable - a leaked global otherwise surfaces only as an unrelated later failure that depends on collection order, which is the hardest kind to attribute.scripts/timezone_data.pyare each enforced in exactly one place, and now have tests.ZoneCollection.validate_structureandzone_positionseach walkedpoly_zone_idselement by element checking it was non-decreasing and each raised the same message built from its own locals; the scan moves into one_validate_non_decreasinghelper andzone_positionsdrops its copy, which could only ever have fired if a caller mutated the array in place - the validator runs at construction and nothing writes to it afterwards. Aif min_zone_id < 0branch is deleted as unreachable: the same method rejects any non-unsigned dtype a dozen lines earlier, so it read as the guard against negative zone ids while being incapable of firing. The class had no tests at all, so what it actually promises - the unsigned-dtype rejection that makes a negative id unrepresentable, the ordering and maximum-id rules, and the shapezone_positionsreturns - is now pinned bytests/timezone_data_test.py.tests/locations.pyinstead of verbatim in bothtests/main_test.pyandtests/utils_test.py, where only one copy carried the comment explaining what makes them interesting and adding a corner to it left the other testing a smaller set.scripts/shortcuts.pyis annotated for what it is actually passed. Both annotations were the wrong way round:check_shortcut_sortingdeclarednp.ndarrayand only ever receives thelist[int]thatoptimise_shortcut_orderingreturns, and it hands thenp.ndarrayit derives tohas_coherent_sequences(lst: list[int]). Widened rather than swapped, sincetests/shortcut_test.pycalls the latter with real lists.requires-pythonand one classifier per minor version inpyproject.toml, thepy{...}factors oftox.ini's envlist, the test matrix andCIBW_BUILD_VERSIONSinbuild.yml, andpy_limited_apiinsetup.py- and two "must match" comments said so while nothing enforced them.tests/test_python_version_support.pyfails when they drift, in either of the two directions that fail silently: a classifier added without a matrix entry ships a version the package claims to support and CI never runs, and arequires-pythonraised without moving the abi3 base builds wheels tagged for an interpreter that is no longer supported. Each assertion was checked against the specific one-sided edit it targets, and both comments now name the test.calculate_shortcut_index_statstook the number of H3 cells existing at the shortcut resolution from a ladder of literals covering resolutions 0 to 4 and fell through, for anything else, to the number of cells actually stored - which reports coverage of exactly 100 % instead of failing - behind anexcept ImportErrorthat cannot fire, since h3 is a runtime dependency rather than an optional one. It asksh3.get_num_cells, which returns precisely the numbers that were tabulated. Running mypy overscripts/reporting.py, which the pre-commit hook excludes, found seventeen further disagreements between the module and its own signatures: the statistics bag was typed as holding scalars while returning two distributions,load_binary_data's nine-key result was a baredictindexed by string literal, the table renderer declared string rows while stringifying whatever it is handed,mainwas annotatedNonewhile returning exit codes toexit(), andprint_polygon_distribution_tabledocumented a return value it never produced while its one caller discarded it. The two dict results are nowTypedDict\ s inscripts/configs.py, carrying tests that assert their keys against what is really returned, since CI cannot type-checkscripts/. The polygon count that labels a distribution row is no longer formatted into that label and parsed back out of it to key the example lookup.docs/data_report.rstand the benchmark reports regenerate byte-identically throughout.scripts/utils.pyand theimport picklethey kept alive, thei8dtype shim intimezonefinder/_numba_replacements.pythat the no-numba fallback never imports, and a test helper self-documented as kept for future reference - and a guard inscripts/hex_utils.pythat could not fire.Hex.poly_candidatesre-read its cache after initialising it and returned an empty set if it were still unset, which no path through_init_candidatesleaves it: an empty set there means "no candidate polygons", so a converter bug would have surfaced as silently missing shortcuts rather than as a failure. The property had no direct test, being reached only through shortcut generation, and now has one._memory_mode_labellooks its two labels up inPARAM_LABELSinstead of spelling them out, so renaming the display vocabulary can no longer leave the comparison bullets and the tables above them disagreeing.make parseandmake testparserun again. Both invokedscripts/file_converter.pyby path, which putsscripts/onsys.path[0]instead of the repository root, so the converter's ownfrom scripts.timezone_data import ...raisedModuleNotFoundErrorbefore any work started - a total failure that CI never sees, since it runs neither target.make testparseis the only cheap end-to-end exercise of the converter (update_data.shneeds a ~55 MB download), and nothing undertests/coversparse_data(), so while it was broken the converter had no smoke test at all. The invocation documented in the usage docs had the same defect and is now thepython -m scripts.file_converterform thatupdate_data.shalready used;tests/test_script_invocations.pyfails if a by-path invocation returns. Note thatparse_data()writes its report to the checkout's committeddocs/data_report.rstwhatever-outit is given, somake testparseleaves that file describing the three-zone fixture - the target now says so.scripts/is type-checked by the mypy pre-commit hook instead of being excluded from it. The directory holds the data converter and the benchmark tooling - most of the repository's non-library Python - and with nothing running mypy over it the annotations had drifted to fifteen errors: two# type: ignorecodes mypy no longer emits, so the ignore silenced nothing; two implicitOptionaldefaults thatno_implicit_optional = truewas already configured to reject; a dict annotated with a narrower value type than it is assigned; a bucket key and four bounding-box lists annotatedintwhileBoundariesdeclaresfloat; and two missing variable annotations. All fixed as annotations, with no runtime change. Two of the four errors mypy reported intests/auxiliaries.py, which it reaches by following imports out ofscripts/, are fixed alongside.test_scripts_are_type_checked_by_the_hookguards the exclude, which is a quieter way to stop type-checking a directory than theignore_errorslist the neighbouring tests already cover: it takes no override entry and reports nothing.__del__cleanup tests that differed only in which exceptioncleanup()raised, and whether zero or oneResourceWarningwas expected, are two parametrized tests over the suppressed and warned exception tuples. Each previously repeated the same subclass, the samecatch_warningsblock and the same filter, so adding a ninth exception to__del__'s suppression list meant copying the block a ninth time and a copy asserting the wrong count would be invisible. Coverage rises rather than falls: the hand-rolled loop asserting that__del__never raises to user code now runs over all six exception types instead of four.has_coherent_sequencesbuilt an iterator solely to take its first element and then looped from the start anyway (correct, but it reads as an off-by-one),compile_bboxesunpacked a pair and immediately reassigned half of it, andprocess_single_hexreturned thehex_idit was handed so its only caller reassigned the loop variable to itself. Two shadowed builtins (diras a loop variable,idas a parameter) are renamed and three bare generator signatures annotated. The benchmark renderer classifies its "other" group by name suffix, as the two lines above it do, rather than by deep-equality scan over lists of dicts; and thecheck-manifestignore list drops two entries naming files that do not exist (CONTRIBUTING.rst,publish.py). Every converter change was verified by parsingtests/test_input.jsonbefore and after and comparing the outputs byte for byte.twilio/twilio-python (twilio)
v9.13.0Compare Source
Library - Chore
Twiml
<Say>verb.partialPromptOnHangup,eagerEotThreshold,asrSmartFormat,ttsTextNormalization,ttsLanguageTextNormalization,elevenlabsLanguageTextNormalization,includeInPrompt,customerName,applicationReconnect,echoPromptFiltering,turnDetectionattributes to<ConversationRelay>, andhints,asrSmartFormat,ttsTextNormalization,ttsLanguageTextNormalizationattributes to<Language>Api
API Changes
2026-09-22
criteriafield description.2026-09-18
readOnlyon the snapshot count fields (profileCount,partitionCount) from the shared schema to the response usage sites, so the counts stay read-only in responses (no change for API consumers).2026-09-17
Get*operations toFetch*to match the operationId standard:FetchCohort,FetchSnapshot,FetchOperation.ExpressionIdschema and made the operation error example cohort-native, so noaud_identifiers remain in the spec.libraryVisibility: hiddenacross the whole surface (info, path items, and operations).2026-09-08
api.twilio.com/v1surface.2026-08-31
api.twilio.com/v1.2026-08-26
2026-08-19
downstreamServiceNametoaudiences-domain.2026-08-05
api.unified.version.previewbeta feature.2026-07-30
2026-07-29
2026-07-20
2026-07-06
2026-06-23
storeTypeenum fromMEMORAtoTWILIO.2026-05-04
x-rate-limitto the audiences API.2026-04-08
API Changes
2026-10-28
2026-10-27
2026-09-28
/v1/RedactionConfigurations) as a private beta in dev-us1, gated by theapi.redaction.enabledbeta feature./v1/RedactionCatalog), under the same beta feature. It is restricted to One Console.ApplicationSidparameter to TwimlSession and TwimlProcess Create endpointsAudiences
2026-10-23
AdminFetchRecordAdminListCohortsAdminListCohortSnapshotsAdminFetchCohortOperationAdminListSnapshotOperationsOperationStatusCohort: tagsCohort: labelsCohortSnapshot: tagsCohortSnapshot: labels2026-10-23
ListCohortsCreateCohortUpdateCohortListCohortSnapshotsCreateCohortSnapshotFetchCohortOperationOperationStatusCohort: tagsCohort: labelsCohortSnapshot: tags2026-10-14
2026-10-07
Conversations
EMAILto the Conversations v2 Communication channel enum.Deletions
2026-10-28
stage-us1realm.Destinations
2026-11-03
Iam
Instrumentation
API Changes
2026-10-12
2026-10-27
dev-us1), matching the deployed service:201with the event producer (ETag,Idempotent-Replayed), not202with an operation200with the event producer (ETag), not202;If-Matchis its only header parameter201with the attachment (Idempotent-Replayed), not2022026-10-21
/v1/ControlPlane/…), enabled indev-us1only:/v1/ControlPlane/EventProducers(CreateEventProducer, ListEventProducers)/v1/ControlPlane/EventProducers/{producerId}(FetchEventProducer, PatchEventProducer, DeleteEventProducer)/v1/ControlPlane/EventProducers/{producerId}/WriteKeys(CreateWriteKey, ListWriteKeys)/v1/ControlPlane/EventProducers/{producerId}/WriteKeys/{writeKey}(DeleteWriteKey)/v1/ControlPlane/EventProducers/{producerId}/EventSchemas(AttachEventProducerSchemaVersion, ListEventProducerSchemaAttachments)/v1/ControlPlane/EventProducers/{producerId}/EventSchemas/{schemaId}/Versions/{version}(DetachEventProducerSchemaVersion)/v1/ControlPlane/EventSchemas(CreateEventSchema, ListEventSchemas)/v1/ControlPlane/EventSchemas/{schemaId}(FetchEventSchema, PatchEventSchema, DeleteEventSchema)/v1/ControlPlane/EventSchemas/{schemaId}/Versions(ListEventSchemaVersions)/v1/ControlPlane/EventSchemas/{schemaId}/Versions/{version}(FetchEventSchemaVersion, DeleteEventSchemaVersion)/v1/ControlPlane/Operations/{operationId}(FetchControlPlaneOperationStatus) to the new operation shape; nowdev-us1onlyKnowledge
2026-10-27
/v2/ControlPlane/KnowledgeMigrations/{accountSid}/Limits(AdminFetchKnowledgeMigrationLimits)2026-10-21
AdminListKnowledgeMigrations: thestatequery parameter is optional; without it the list returns every account with knowledge basesMigrationSummary: knowledgeBaseCount2026-10-19
/v2/ControlPlane/KnowledgeMigrations(AdminListKnowledgeMigrations)/v2/ControlPlane/KnowledgeMigrations/{accountSid}(AdminFetchKnowledgeMigration)/v2/ControlPlane/KnowledgeMigrations/{accountSid}/History(AdminListKnowledgeMigrationHistory)/v2/ControlPlane/KnowledgeMigrations/{accountSid}/Readiness(AdminFetchKnowledgeMigrationReadiness)/v2/ControlPlane/KnowledgeMigrations/{accountSid}/Runs(AdminListKnowledgeMigrationRuns)/v2/ControlPlane/KnowledgeMigrations/{accountSid}/Transitions(AdminListKnowledgeMigrationTransitions, AdminCreateKnowledgeMigrationTransition)/v2/ControlPlane/KnowledgeMigrations/{accountSid}/Transitions/{transitionId}(AdminFetchKnowledgeMigrationTransition)Lookups
Memory
2026-10-14
dataAsOfto theStoreschema returned byAdminFetchStore.2026-10-14
dataAsOfto theStoreschema returned byFetchStore.2026-10-08
FetchProfileOperation(GET /v1/ProfileOperations/{operationId}) to check the status andRetry-Afterheader withapi.memory.2026Q3beta feature.202responses ofUpdateProfilesBulkandCreateProfilesImportV2now returnoperationId,createdAtandstatusUrl(behind theapi.memory.2026Q3beta feature), plus theOperation-Id,LocationandRetry-Afterresponse headers. UsestatusUrlwithFetchProfileOperationtoMessaging
GET/PUT /v1/Accounts/{AccountSid}/DataAccessPolicy) to the messaging admin-api, backed bypostflight-messaging-api(dev-us1 and stage-us1 only)GET/PUT /v1/Accounts/{AccountSid}/DataAccessPolicy) in prod-us1 and prod-ie1Segment
API Changes
2026-11-04
GET /v1/Segment/Cob/Whoami,/Records,/Audits,/Audits/{Family}/{Name}2026-10-30
GET /v1/Whoami(One Admin identity check for the Segment oneadmin-gateway)Trusthub
Voice
Webhooks
2026-10-28
prefixandvalueare limited to 1000 characters. Aprefixmust end with/and avaluemust not.2026-10-13
betatoga. Library visibility changed fromprivatetopublic.v9.12.0Compare Source
Audiences
2026-10-06
2026-10-05
2026-10-02
/preview/Cohorts/{cohortId}/Operations(AdminListCohortOperations)2026-10-01
AdminListSnapshotOperations2026-09-28
AdminListCohortSnapshotsAdminFetchCohortSnapshotCriteriaOperationPayload: cohortIdOperationPayload: audienceIdOperationResultOperationResult: cohortIdOperationResult: audienceIdRecordCohort2026-10-01
2026-09-28
CohortOperationResultPartitionCountConversations
CANCELEDimport status (dev-us1 and stage-us1)traitExtractionStrategyIdsandobservationExtractionStrategyIdsto the Conversations v2 Admin Configuration schema.domainIdfield.Data-ingress
2026-10-01
2026-09-17
2026-10-01
2026-09-17
Events
/v1/Replays, beta featureapi.eventstreams.replay); the feature was never GA and is being sunset (breaking change)Knowledge
2026-09-24
downstreamServiceNamefrommemora-domaintoenterprise-knowledge-domainfor all operationsMemory
2026-09-30
sourcefield on observations and conversation summaries, and thesourcefilterListProfileObservations, now also accept forward slashes (/). The pattern is^[a-zA-Z0-9 _./-]+$. This is additive and backwards compatible: every previouslyUpdateStore,UpdateTraitGroup, andUpdateDataMappingnow require a body with at least one property. An empty{}gets a 400.Messaging
GET/PUT/DELETE /v1/Accounts/{AccountSid}/OptOuts/{OptOutSid}/...) to the messaging admin-api (dev-us1 and stage-us1 only)POST /v1/Accounts/{AccountSid}/OptOuts) to the messaging admin-api (dev-us1 and stage-us1 only)PUT /v1/Accounts/{AccountSid}/Services/{MessagingServiceSid}) to the messaging admin-api (dev-us1 and stage-us1 only)pending_display_name,pending_display_name_statusandpending_display_name_status_dateon fetch, anddisplay_name_statuson update and partial updateRoutes
GET/POST/v3/ShortCodes/{isoCountryCode}/{shortCode}topublic, maturity togav9.11.2Compare Source
Twiml
audio_formatandsample_rateto Stream for HDCodecApi
API Changes
2026-09-22
criteriafield description.2026-09-18
readOnlyon the snapshot count fields (profileCount,partitionCount) from the shared schema to the response usage sites, so the counts stay read-only in responses (no change for API consumers).2026-09-17
Get*operations toFetch*to match the operationId standard:FetchCohort,FetchSnapshot,FetchOperation.ExpressionIdschema and made the operation error example cohort-native, so noaud_identifiers remain in the spec.libraryVisibility: hiddenacross the whole surface (info, path items, and operations).2026-09-08
api.twilio.com/v1surface.2026-08-31
api.twilio.com/v1.2026-08-26
2026-08-19
downstreamServiceNametoaudiences-domain.2026-08-05
api.unified.version.previewbeta feature.2026-07-30
2026-07-29
2026-07-20
2026-07-06
2026-06-23
storeTypeenum fromMEMORAtoTWILIO.2026-05-04
x-rate-limitto the audiences API.2026-04-08
TwimlProcessStatusCallbackEventsparameter to CreateTwimlProcess endpointtwiml_processresource alongsidetwiml_session(twiml_sessionis deprecated and will be removed in a future release)Audiences
2026-09-14
2026-09-11
AdminFetchRecordAdminFetchCohortAdminFetchCohortSnapshotAdminFetchCohortOperationResourceLabels2026-09-22
Criteria2026-09-18
2026-09-17
2026-09-17
CreateCohortListCohortSnapshotsCreateCohortSnapshotFetchCohortSnapshotListCohortSnapshotProfilesCriteriaCohortCohortSnapshotCohortOperationResultCohortOperation2026-09-15
2026-09-14
FetchCohortFetchCohortSnapshotFetchCohortOperationResourceLabelsCapabilities
Conversations
Data-ingress
2026-09-07
2026-09-09
/v1/DataSyncs/{syncId}(FetchDataSync)/v1/CloudAppSources/{sourceId}/Objects(ListCloudAppObjects)/v1/WarehouseSources/{sourceId}/Preview(CreateWarehousePreview)/v1/WarehouseSources/{sourceId}/Preview/{operationId}(FetchWarehousePreview)/v1/DataSample/{operationId}(FetchDataSample)/v1/ControlPlane/CloudAppSources/{sourceId}(FetchCloudAppSource, PatchCloudAppSource, DeleteCloudAppSource)/v1/ControlPlane/CloudAppSources/{sourceId}/Datasets(ListCloudAppDatasets, CreateCloudAppDataset)/v1/ControlPlane/CloudAppSources/{sourceId}/Datasets/{datasetId}(FetchCloudAppDataset, PatchCloudAppDataset, DeleteCloudAppDataset)/v1/ControlPlane/WarehouseSources/{sourceId}(FetchWarehouseSource, PatchWarehouseSource, DeleteWarehouseSource)/v1/ControlPlane/WarehouseSources/{sourceId}/Datasets(ListWarehouseDatasets, CreateWarehouseDataset)/v1/DataSyncs/{SyncId}(FetchDataSync)/v1/CloudAppSources/{SourceId}/Objects(ListCloudAppObjects)/v1/WarehouseSources/{SourceId}/Preview(CreateWarehousePreview)/v1/WarehouseSources/{SourceId}/Preview/{OperationId}(FetchWarehousePreview)/v1/DataSample/{OperationId}(FetchDataSample)/v1/ControlPlane/CloudAppSources/{SourceId}(FetchCloudAppSource, PatchCloudAppSource, DeleteCloudAppSource)/v1/ControlPlane/CloudAppSources/{SourceId}/Datasets(ListCloudAppDatasets, CreateCloudAppDataset)/v1/ControlPlane/CloudAppSources/{SourceId}/Datasets/{DatasetId}(FetchCloudAppDataset, PatchCloudAppDataset, DeleteCloudAppDataset)/v1/ControlPlane/WarehouseSources/{SourceId}(FetchWarehouseSource, PatchWarehouseSource, DeleteWarehouseSource)/v1/ControlPlane/WarehouseSources/{SourceId}/Datasets(ListWarehouseDatasets, CreateWarehouseDataset)Destinations
2026-09-24
GETfrom themethodenum on thewebhookdestination type's config options2026-09-22
CDPto thecategoriesenum onDestinationType2026-09-14
2026-09-11
CreateDestinationUpdateDestinationDeleteDestinationCredentialInputDestinationCredentialOptions2026-09-11
ListDestinationTypesGetDestinationTypeCreateDestinationUpdateDestination2026-09-10
ListDestinationTypesandGetDestinationTypeunauthenticated (security: []).iamOperation,addAuthorizationHeader, andbetaFeaturefrom these operations.Iam
Intelligence
rule.operators[]parameter override field to `POST /v3/RuleExecutionsPOST /v3/RuleExecutionsKnowledge
2026-09-23
WebSourceDetails: errorsdownstreamServiceNamefrommemora-domaintoenterprise-knowledge-domainfor all operationsMemory
2026-09-18
/v1/ControlPlane/TraitExtractionStrategies(AdminListTraitExtractionStrategies)/v1/ControlPlane/TraitExtractionStrategies/{traitStrategyId}(AdminFetchTraitExtractionStrategy)2026-09-03
DataMappingCoredescription: the PATCH body requires at least one field.2026-09-10
/v1/ControlPlane/ObservationExtractionStrategies(ListObservationExtractionStrategies, CreateObservationExtractionStrategy)/v1/ControlPlane/ObservationExtractionStrategies/{observationExtractionStrategyId}(FetchObservationExtractionStrategy, UpdateObservationExtractionStrategy, DeleteObservationExtractionStrategy)CreateObservationExtractionStrategyInputdescription to document that at least one ofallowedCategories/prohibitedCategoriesmust be provided with a non-empty list2026-09-07
/v1/Stores/{storeId}/Profiles/BulkDeletions(CreateProfilesBulkDeletion)2026-09-03
UpdateStore,UpdateTraitGroup,UpdateDataMapping, andUpdateTraitExtractionStrategynow require a body with at least one property. An empty{}gets a 400.Routes
GET/POST/v3/ShortCodes/{isoCountryCode}/{shortCode}to assign and fetch the Inbound Processing Region for short codesVerify
aaguidanddate_last_approvedto passkeys factorsmetadata.Voice
Webhooks
2026-09-24
GET /v1/Webhooks/Settingsnow documents each item as the fullSettingrather thanSettingSummary, addingauth,signature,connectionandedgeZonesto the list response. The service already returned these fields; only the schema understated them. Additive for consumers — no field is removed or renamed. The now-unreferencedSettingSummaryschema is removed.v9.11.1Compare Source
Library - Fix
Audiences
2026-09-01
FetchCohortSnapshotoperation.2026-08-26
/preview/Audiences(AdminListAudiences)/preview/Audiences/{audienceId}(AdminGetAudience)/preview/Snapshots(AdminListSnapshots)/preview/Snapshots/{snapshotId}(AdminGetSnapshot)/preview/Operations/{operationId}(AdminGetOperation)2026-08-25
2026-08-24
/preview/Snapshots/{cohortSnapshotId}/Operations(AdminListSnapshotOperations)/preview/Snapshots/{snapshotId}/Operations(ListAdminSnapshotOperations)2026-08-20
AdminGetCohort->FetchAdminCohort) to meet standard.infolibraryVisibilitytohiddento exclude this admin spec from generation.pageSize/pageTokenquery parameters toListAdminSnapshotOperations, the only list operation missing them.2026-08-19
/preview/Cohorts(AdminListCohorts)/preview/Cohorts/{cohortId}(AdminGetCohort)/preview/CohortSnapshots(AdminListCohortSnapshots)/preview/CohortSnapshots/{cohortSnapshotId}(AdminGetCohortSnapshot)/preview/CohortOperations/{cohortOperationId}(AdminGetCohortOperation)2026-09-01
Get*operations toFetch*to match the operationId standard:FetchCohort,FetchCohortSnapshot,FetchCohortOperation. The transpiler skips operations whose operationId does not start with a standard keyword, which had been dropping all three from generated output.libraryVisibilitytoprivate(washidden) so the spec is eligible for the private docs pipeline.2026-08-28
/preview/Audiences(ListAudiences, CreateAudience)/preview/Audiences/{audienceId}(FetchAudience, UpdateAudience, DeleteAudience)/preview/Snapshots(ListSnapshots, CreateSnapshot)/preview/Snapshots/{snapshotId}(FetchSnapshot, DeleteSnapshot)/preview/Snapshots/{snapshotId}/Profiles(ListSnapshotProfiles)/preview/Operations/{operationId}(FetchOperation)2026-08-20
Get*operations toFetch*to match the operationId standard./preview/Operations/{operationId}.Conversations
VIDEOto the Conversations v2 Communication channel enum.Data-ingress
API Changes
2026-09-01
2026-08-12
2026-08-12
Destinations
2026-09-08
prod-ie1tosupportedRealmsandiamOperationEnabledRealmsfor all endpoints2026-09-01
admin-apiplaceholder fromsupportedRealmson the public endpoints;admin_openapi.yaml./v1/ControlPlane/Destinations(AdminListDestinations)/v1/ControlPlane/Destinations/{destinationId}(AdminGetDestination)/v1/ControlPlane/Subscriptions(AdminListSubscriptions)/v1/ControlPlane/Subscriptions/{subscriptionId}(AdminGetSubscription)/v1/ControlPlane/Subscriptions/{subscriptionId}/EventTypes(AdminListSubscribedEvents)/v1/ControlPlane/Subscriptions/{subscriptionId}/EventTypes/{eventType}(AdminGetSubscribedEvent)2026-08-26
2026-08-26
2026-08-25
CreateDestination2026-08-12
2026-08-12
DestinationType: releaseStatusDestinationType: maturityEmail
API Changes
2026-09-02
/v1/Sends/Cohorts(sendCohort)2026-08-28
sendTransactionalsendTransactional: X-Twilio-VersionSuppressionsGroupSuppressionsGroup: typeSuppressionsGroup: modeSuppressionsGlobalSuppressionsGlobal: typeSuppressionsGlobal: modeSuppressionsLongRunningOperationResponse2026-08-27
SendEmailSendEmail: Content-Encoding, Idempotency-KeyEnvelopeSuppressionsGroupSuppressionsGlobalIam
Instrumentation
API Changes
2026-09-04
IdempotencyKeyHeaderto Create/Patch/DeleteAutoInstrumentationRule; corrected the shared header's descriptionoperationId/createdAttoLongRunningOperationResponse; corrected examplestatusfromRUNNINGtoPENDINGSignal.timestamp→occurredAt,HourlyStats.hourTimestamp→hourAt; uppercasedSignal.typeandListSignals'signalTypeenums to SCREAMING_SNAKE_CASEUserBehaviorsrequest/response fields to camelCase; added thesummaryDeliverywebhook callbackPaginationMeta(added requiredkey, correctedpageSizebounds) and all 6 list operations (ListEventSources,ListEventSourceDatasets,ListEventSchemas,ListAutoInstrumentationRules,ListSignals,ListSignalStats) to themetaenvelope; addedListSignalStats'500responsesupportedRealmstodev-us1only;POST /v1/UserBehaviors(AnalyzeUserBehaviors) keeps its owndev-us1/stage-us1override (prod withheld pending stage validation) — every other operation is now dev-only2026-08-31
tdi_TTID prefix throughout (path params, examples, transaction URLs) toevents_; unified dataset/schema ID formats (tdi_dat_/tdi_dataset_→events_dataset_,tdi_schema_→events_evsch_); fixed rule-version examples to match the realversion: integerfieldoperationIdto use the domain-agnosticproc_job_TTID prefix; corrected theOperationIdparameter's length constraint (max=34→max=35) and added apatterntdi_prefix length:IngestEventBatch'ssourceIdpath parameter (maxLength37 → 40); removed the stale, redundantTwilio-Write-Keyheader parameterAutoInstrumentationRuleIdregexpatterninfo.title("Twilio Data Ingress - Instrumentation API" → "Events Domain - Instrumentation API")/v1/UserBehaviors(AnalyzeUserBehaviors)2026-07-28
/v1/ControlPlane/…):/v1/ControlPlane/EventSources(CreateEventSource, ListEventSources)/v1/ControlPlane/EventSources/{sourceId}(FetchEventSource, PatchEventSource, DeleteEventSource)/v1/ControlPlane/EventSources/{sourceId}/WriteKeys(CreateWriteKey, ListWriteKeys)/v1/ControlPlane/EventSources/{sourceId}/WriteKeys/{writeKey}(DeleteWriteKey)/v1/ControlPlane/EventSources/{sourceId}/Datasets(CreateEventSourceDataset, ListEventSourceDatasets)/v1/ControlPlane/EventSources/{sourceId}/Datasets/{datasetId}(FetchEventSourceDataset, PatchEventSourceDataset, DeleteEventSourceDataset)/v1/ControlPlane/EventSources/{sourceId}/EventSchemas(CreateEventSchema, ListEventSchemas)/v1/ControlPlane/EventSources/{sourceId}/EventSchemas/{schemaId}(FetchEventSchema, PatchEventSchema, DeleteEventSchema)/v1/ControlPlane/EventSources/{sourceId}/AutoInstrumentationRules(CreateAutoInstrumentationRule, ListAutoInstrumentationRules)/v1/ControlPlane/EventSources/{sourceId}/AutoInstrumentationRules/{autoInstrumentationRuleId}(FetchAutoInstrumentationRule, PatchAutoInstrumentationRule, DeleteAutoInstrumentationRule)/v1/ControlPlane/EventSources/{sourceId}/AutoInstrumentationRules/{autoInstrumentationRuleId}/Versions(ListAutoInstrumentationRuleVersions)/v1/ControlPlane/EventSources/{sourceId}/AutoInstrumentationRules/{autoInstrumentationRuleId}/Versions/{version}(FetchAutoInstrumentationRuleVersion)/v1/ControlPlane/Datasets(ListDatasets)/v1/ControlPlane/Datasets/{datasetId}(FetchDataset)/v1/ControlPlane/Operations/{operationId}(FetchControlPlaneOperationStatus)/v1/ControlPlane/Datasets(ListDatasets)/v1/ControlPlane/Datasets/{datasetId}(FetchDataset)/v1/EventSources/…):/v1/EventSources/{sourceId}/Batch(IngestEventBatch)/v1/EventSources/{sourceId}/AutoInstrumentationRules/{autoInstrumentationRuleId}/Preview(TriggerAutoInstrumentationPreview)/v1/EventSources/{sourceId}/AutoInstrumentationRules/{autoInstrumentationRuleId}/Preview/{operationId}(GetAutoInstrumentationPreviewResult)/v1/EventSources/…):/v1/EventSources/{sourceId}/Signals(IngestSignals, ListSignals)/v1/EventSources/{sourceId}/Signals/{signalKey}(GetSignalBySignalKey)/v1/EventSources/{sourceId}/SignalStats(ListSignalStats)Knowledge
2026-07-20
KnowledgeErrorInstance,KnowledgeErrorGrouperrorsfield toWebSourceDetailsfor reporting web crawl errorsMemory
2026-08-18
CSVandDATASETvalues from theDataMappingTypeenum.INGRESS,DATASET_CLOUDAPP, andDATASET_WAREHOUSEare the only valid values now.DataMappingFromCSVandDataMappingFromDataSetschemas and theironeOf/discriminator entries onDataMappingFromTypes, along with the correspondingCSV/DATASETdiscriminator mapping keys.type: CSVortype: DATASETonAdminListDataMappings2026-08-10
DataMappingTypegainsINGRESS,DATASET_CLOUDAPP, andDATASET_WAREHOUSE.CSVandDATASETremain valid and unchanged; they are deprecated aliases andDataMappingFromTypesgains threeoneOfmembers and three discriminator keys:DataMappingFromIngress(renamesDataMappingFromCSV),DataMappingFromCloudAppDataSetandDataMappingFromWarehouseDataSetDataMappingFromDataSet, distinguishing a cloud-app-backedCSVandDATASETpayloads are2026-08-27
/v1/ControlPlane/TraitExtractionStrategies(ListTraitExtractionStrategies, CreateTraitExtractionStrategy)/v1/ControlPlane/TraitExtractionStrategies/{traitStrategyId}(FetchTraitExtractionStrategy, UpdateTraitExtractionStrategy, DeleteTraitExtractionStrategy)2026-08-18
CSVandDATASETvalues from theDataMappingTypeenum.INGRESS,DATASET_CLOUDAPP, andDATASET_WAREHOUSEare the only valid values now.DataMappingFromCSVandDataMappingFromDataSetschemas and theironeOf/discriminator entries onDataMappingFromTypes, along with the correspondingCSV/DATASETdiscriminator mapping keys.type: CSVortype: DATASETonCreateDataMappingorUpdateDataMapping(or filteringListDataMappings/ListDataMappingSuggestionsby2026-08-10
DataMappingTypegainsINGRESS,DATASET_CLOUDAPP, andDATASET_WAREHOUSE.CSVandDATASETremain valid and unchanged; they are deprecated aliases andDataMappingFromTypesgains threeoneOfmembers and three discriminator keys:DataMappingFromIngress(renamesDataMappingFromCSV),DataMappingFromCloudAppDataSetandDataMappingFromWarehouseDataSetDataMappingFromDataSet, distinguishing a cloud-app-backedCSVandDATASETpayloads areMessaging
/v1/Channels/WhatsApp/Senders) from RestProxy; the sender was routed to the sunsettingmessaging-whatsapp-k8s-orchdownstream. Use the Senders v2 API (/v2/Channels/Senders) instead.Verify
Templatesoptional parameter on Verification creation (a stringified JSON array ofsid/substitutionsentries). When provided,Templatestakes precedence overTemplateSid.Voice
2026-09-03
links.conversationto Transcription resources as the absolute Conversations API URL for theconversationId. It is present once the transcript has been stored; thelinksobject2026-09-01
mediaUrlfromCreateRequestWithMediaUrl's required fields so a request with neithersourceIdnormediaUrlreaches the downstream service, which rejects it with the specific error code 17500 instead of a generic gateway 400additionalProperties: falseon bothCreateRequestWithSourceIdandCreateRequestWithMediaUrlso the twooneOfvariants stay mutually exclusive:sourceIdis undeclared on the media-URL variant andmediaUrlis undeclared on the source-ID variant, keeping every request shape resolvable to exactly one variant2026-08-05
Webhooks
API Changes
2026-08-24
v9.11.0Compare Source
Library - Fix
Twiml
<Assistant>noun from<Connect>verb as part of the AI Assistants deprecation (breaking change)passportsattribute to<Dial>verb for SHAKEN/STIR passport passthroughAccounts
SuppressEmailNotificationparameter to the Secondary Auth Token and Auth Token promotion endpoints. Set it totrueto suppress the email notification sent to account owners and administrators. Defaults tofalse, preserving existing behavior.Ai
Api
uriproperty to thetwiml_sessionresourceData-ingress
2026-08-07
/v1/DataQuery(Realtime DataQuery)2026-07-07
/v1/DataQuery(Realtime DataQuery)2026-06-17
OAuthJWTBearerCredentials: privateKey, privateKeyPassphrase2026-06-12
/v1/DataSyncs/{syncId}(FetchDataSync)/v1/CloudAppSources/{sourceId}/Objects(ListCloudAppObjects)/v1/WarehouseSources/{sourceId}/Preview(CreateWarehousePreview)/v1/WarehouseSources/{sourceId}/Preview/{operationId}(FetchWarehousePreview)/v1/DataSample/{operationId}(FetchDataSample)/v1/ControlPlane/CloudAppSources/{sourceId}(FetchCloudAppSource, PatchCloudAppSource, DeleteCloudAppSource)/v1/ControlPlane/CloudAppSources/{sourceId}/Datasets(ListCloudAppDatasets, CreateCloudAppDataset)/v1/ControlPlane/CloudAppSources/{sourceId}/Datasets/{datasetId}(FetchCloudAppDataset, PatchCloudAppDataset, DeleteCloudAppDataset)/v1/ControlPlane/WarehouseSources/{sourceId}(FetchWarehouseSource, PatchWarehouseSource, DeleteWarehouseSource)/v1/ControlPlane/WarehouseSources/{sourceId}/Datasets(ListWarehouseDatasets, CreateWarehouseDataset)/v1/DataSyncs/{SyncId}(FetchDataSync)/v1/CloudAppSources/{SourceId}/Objects(ListCloudAppObjects)/v1/WarehouseSources/{SourceId}/Preview(CreateWarehousePreview)/v1/WarehouseSources/{SourceId}/Preview/{OperationId}(FetchWarehousePreview)/v1/DataSample/{OperationId}(FetchDataSample)/v1/ControlPlane/CloudAppSources/{SourceId}(FetchCloudAppSource, PatchCloudAppSource, DeleteCloudAppSource)/v1/ControlPlane/CloudAppSources/{SourceId}/Datasets(ListCloudAppDatasets, CreateCloudAppDataset)/v1/ControlPlane/CloudAppSources/{SourceId}/Datasets/{DatasetId}(FetchCloudAppDataset, PatchCloudAppDataset, DeleteCloudAppDataset)/v1/ControlPlane/WarehouseSources/{SourceId}(FetchWarehouseSource, PatchWarehouseSource, DeleteWarehouseSource)/v1/ControlPlane/WarehouseSources/{SourceId}/Datasets(ListWarehouseDatasets, CreateWarehouseDataset)2026-06-11
/v1/EventSources/{sourceId}/Signals(ListSignals)/v1/EventSources/{sourceId}/Signals/{signalKey}(GetSignalBySignalKey)/v1/EventSources/{sourceId}/SignalStats(ListSignalStats)2026-05-26
/v1/ControlPlane/EventSources(CreateEventSource, ListEventSources)/v1/ControlPlane/EventSources/{SourceId}(FetchEventSource, PatchEventSource, DeleteEventSource)/v1/ControlPlane/EventSources/{SourceId}/WriteKeys(CreateWriteKey, ListWriteKeys)/v1/ControlPlane/EventSources/{SourceId}/WriteKeys/{WriteKey}(DeleteWriteKey)/v1/ControlPlane/EventSources/{SourceId}/Datasets(CreateEventSourceDataset, ListEventSourceDatasets)/v1/ControlPlane/EventSources/{SourceId}/Datasets/{DatasetId}(FetchEventSourceDataset, PatchEventSourceDataset, DeleteEventSourceDataset)/v1/ControlPlane/EventSources/{SourceId}/EventSchemas(CreateEventSchema, ListEventSchemas)/v1/ControlPlane/EventSources/{SourceId}/EventSchemas/{SchemaId}(FetchEventSchema, PatchEventSchema, DeleteEventSchema)/v1/ControlPlane/AutoInstrumentationRule(CreateAutoInstrumentationRule, ListAutoInstrumentationRules)/v1/ControlPlane/AutoInstrumentationRule/{AutoInstrumentationRuleId}(FetchAutoInstrumentationRule, PatchAutoInstrumentationRule, DeleteAutoInstrumentationRule)/v1/ControlPlane/AutoInstrumentationRule/{AutoInstrumentationRuleId}/Versions(ListAutoInstrumentationRuleVersions)/v1/ControlPlane/AutoInstrumentationRule/{AutoInstrumentationRuleId}/Versions/{Version}(FetchAutoInstrumentationRuleVersion)2026-06-12
/v1/DataSyncs/{syncId}(FetchDataSync)/v1/CloudAppSources/{sourceId}/Objects(ListCloudAppObjects)/v1/WarehouseSources/{sourceId}/Preview(CreateWarehousePreview)/v1/WarehouseSources/{sourceId}/Preview/{operationId}(FetchWarehousePreview)/v1/DataSample/{operationId}(FetchDataSample)/v1/ControlPlane/CloudAppSources/{sourceId}(FetchCloudAppSource, PatchCloudAppSource, DeleteCloudAppSource)/v1/ControlPlane/CloudAppSources/{sourceId}/Datasets(ListCloudAppDatasets, CreateCloudAppDataset)/v1/ControlPlane/CloudAppSources/{sourceId}/Datasets/{datasetId}(FetchCloudAppDataset, PatchCloudAppDataset, DeleteCloudAppDataset)/v1/ControlPlane/WarehouseSources/{sourceId}(FetchWarehouseSource, PatchWarehouseSource, DeleteWarehouseSource)/v1/ControlPlane/WarehouseSources/{sourceId}/Datasets(ListWarehouseDatasets, CreateWarehouseDataset)/v1/DataSyncs/{SyncId}(FetchDataSync)/v1/CloudAppSources/{SourceId}/Objects(ListCloudAppObjects)/v1/WarehouseSources/{SourceId}/Preview(CreateWarehousePreview)/v1/WarehouseSources/{SourceId}/Preview/{OperationId}(FetchWarehousePreview)/v1/DataSample/{OperationId}(FetchDataSample)/v1/ControlPlane/CloudAppSources/{SourceId}(FetchCloudAppSource, PatchCloudAppSource, DeleteCloudAppSource)/v1/ControlPlane/CloudAppSources/{SourceId}/Datasets(ListCloudAppDatasets, CreateCloudAppDataset)/v1/ControlPlane/CloudAppSources/{SourceId}/Datasets/{DatasetId}(FetchCloudAppDataset, PatchCloudAppDataset, DeleteCloudAppDataset)/v1/ControlPlane/WarehouseSources/{SourceId}(FetchWarehouseSource, PatchWarehouseSource, DeleteWarehouseSource)/v1/ControlPlane/WarehouseSources/{SourceId}/Datasets(ListWarehouseDatasets, CreateWarehouseDataset)Deletions
API Changes
2026-06-23
POST/GET /v1/UserDataDeletions,GET /v1/UserDataDeletions/{deletionId},GET /v1/Operations/{operationId}).Destinations
API Changes
2026-08-07
ListDestinationSupportedEventTypes2026-08-07
ListDestinationSupportedEventTypes2026-08-07
/v1/Catalog/DestinationSupportedEventTypes(ListDestinationSupportedEventTypes)/v1/ControlPlane/Subscriptions/{subscriptionId}/EventTypes(ListSubscribedEvents, CreateSubscribedEvent)/v1/ControlPlane/Subscriptions/{subscriptionId}/EventTypes/{eventType}(GetSubscribedEvent, UpdateSubscribedEvent, DeleteSubscribedEvent)/v1/ControlPlane/Subscriptions/{subscriptionId}/SubscribedEvents(ListSubscribedEvents, CreateSubscribedEvent)/v1/ControlPlane/Subscriptions/{subscriptionId}/SubscribedEvents/{eventType}(GetSubscribedEvent, UpdateSubscribedEvent, DeleteSubscribedEvent)/v1/Catalog/DestinationEventTypes(ListDestinationEventTypes)2026-08-04
2026-08-04
2026-08-04
2026-08-04
2026-08-04
SubscriptionResponse: eventTypes2026-08-03
DestinationType: documentationUrl, maturity, tier, categories2026-07-27
2026-07-23
metais now a required property in the response ofListDestinations,ListSubscriptions, andListSubscribedEventsCreateSubscribedEventrequest body schema consolidated ontoSubscribedEventInput(previouslySubscribedEventCreate, a duplicate schema)ListDestinations,ListSubscriptions, andListSubscribedEvents2026-07-23
/v1/Catalog/DestinationEventTypes(ListDestinationEventTypes)2026-07-22
ListDestinations: namePrefixUpdateSubscriptionSubscriptionUpdate: eventTypes2026-07-16
Events
stage-ie1realm support for EventTypes and Schemas endpoints (datataps-catalog)Memory
2026-08-10
Eventsendpoints from the spec, as they were hidden, never implemented, and are not part of the public API2026-08-07
pageSize,pageToken, andorderByquery parameters toListProfileImportsV2, plus ametaobject in its response, to support pagination.ListProfileImportsV2description to document the new pagination behavior.CreateProfilesImportV2to match the real S3 bucket naming convention.meta.keyexample onListProfileTraits's response (wasprofiles, nowtraits) to accurately describe which response field it points to.2026-08-07
matchingRulesdescription inIdentityResolutionSettingsCoreto remove compoundANDrule documentation2026-07-30
codefrom99999to9999992026-07-28
pageSizedescription on the paginationMetaschema to clarify it reflects the number of items actually returned, not the requested or default page size.2026-07-13
ListProfilesresponse schema references fromProfileID/ProfilesMetatoIdentityProfileID/IdentityProfilesMeta(newIdentityProfilesMetaschema added;ProfileID/ProfilesMetaretained for other operations).2026-07-08
CreateDataMappingSuggestionFetchDataMappingSuggestion2026-07-06
/v1/ControlPlane/Stores/{storeId}/DataMappings/Suggestions(ListDataMappingSuggestions, CreateDataMappingSuggestion)/v1/ControlPlane/Stores/{storeId}/DataMappings/Suggestions/{suggestionId}(FetchDataMappingSuggestion)2026-06-26
instancetolistfor all endpoints that don't end with a /{param}UpdateProfileTraitsUpdateProfileTraitsMappingTraitItem: fieldNameadditionalPropertiesfromallofschemas since it isn't supported and causes invalid lint errors on example blocks.2026-06-24
2026-05-01
UpdateProfileTraitsFetchIdentityResolutionSettingsMappingTraitItem: fieldNameoccurredAt,content, orsource2026-04-28
UpdateProfileTraitsUpdateProfileTraitsTraitDefinition: validationRuleMappingTraitItem: fieldNameMessaging_admin
/v1/Messaging/IntelligentAlertsEvents/*, routing to intelligent-alerts-api via messaging-monkey-backend. Account SID is a required query parameter (accountSid) on all three endpoints.Kludex/uvicorn (uvicorn)
v0.54.0: Version 0.54.0Compare Source
📨 Send metadata after the response body
uvicorn0.54.0 adds response trailers and103 Early Hintsto its experimental HTTP/2 implementation throughzttp.http.response.trailersextension lets applications send metadata, such as checksums, after the response body. Clients must sendTE: trailersto receive them. Multiple trailer messages are combined before completing the response.--http zttp --http2. Upgrade-based h2c and WebSockets over HTTP/2 remain unsupported.💡 Hint at resources before the final response
103 Early Hintsover HTTP/2 (#3137). Applications can use the ASGIhttp.response.early_hintextension to send resource hints before the final response. Each supplied link becomes a separateLinkheader.Full changelog: 0.53.0...0.54.0
v0.53.0: Version 0.53.0Compare Source
🌐 Opt-in HTTP/2 support
uvicorn0.53.0 adds experimental HTTP/2 throughzttp, alongside a newzuvloopintegration and connection-handling improvements.zttp(#2982, #3101). Installzttp, then enable HTTP/2 with--http zttp --http2. Uvicorn negotiates HTTP/2 over TLS with ALPN and supports cleartext prior knowledge.⚙️ More event loop choice
zuvloop(#3104). Installzuvloopseparately and select it explicitly with--loop zuvloopon CPython 3.14 or newer.🛡️ More reliable connections and proxies
Connection: closetoken lists (#3103). Uvicorn now parses comma-separated tokens case-insensitively across HTTP implementations.FORWARDED_ALLOW_IPSvalue now includes::1.Full changelog: 0.52.4...0.53.0
v0.52.4: Version 0.52.4Compare Source
Fixed
Dateheaders from accepted WebSocket handshakes withwebsockets-sansio(#3078)Full Changelog: https://github.com/Kludex/uvicorn/compare/0.52.3...0.52.4
v0.52.3: Version 0.52.3Compare Source
Changed
zttpto 0.0.24 and use its combined receive path, improving HTTP/1.1 request parsing performance (#3067)Full Changelog: https://github.com/Kludex/uvicorn/compare/0.52.2...0.52.3
v0.52.2: Version 0.52.2Compare Source
Fixed
zttpto 0.0.22, fixing bodyless request receives and improving HTTP/1 request parsing performance (#3063)Full Changelog: https://github.com/Kludex/uvicorn/compare/0.52.1...0.52.2
v0.52.1: Version 0.52.1Compare Source
Fixed
websockets-sansioandwsprotoimplementations, waiting for the client's close reply with a 10 second timeout instead of resetting the connection (#3053)websockets-sansioimplementation, preventing data truncation on server-initiated closes with large in-flight payloads (#3048)Content-TypeandContent-Lengthheaders from WebSocket denial responses on thewebsockets-sansioimplementation, and deliver non-UTF-8 denial bodies intact (#3041)Full Changelog: https://github.com/Kludex/uvicorn/compare/0.52.0...0.52.1
v0.52.0: Version 0.52.0Compare Source
This release adds an experimental HTTP/1.1 implementation backed by zttp, a sans-IO HTTP parser I've been developing on the side: a core written in Zig, with bindings to Python. It has been running under a fuzzer for some weeks now, and has been through multiple rounds of security auditing.
It is still experimental, so don't put it in front of production traffic yet. Try it with
--http zttp, and please send any feedback to the issue tracker.Added
zttpHTTP/1.1 implementation, selectable with--http zttp(#2979)Fixed
Full Changelog: https://github.com/Kludex/uvicorn/compare/0.51.0...0.52.0
Configuration
📅 Schedule: (in timezone America/New_York)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
👻 Immortal: This PR will be recreated if closed unmerged. Get config help if that's undesired.
This PR has been generated by Mend Renovate.
b0ec11f77707435a268207435a2682339b1c3c5d339b1c3c5d108a38839c108a38839c7d6f9b16467d6f9b16469b52093c8e9b52093c8e7e374cd9037e374cd903171edff1e9171edff1e9dad1a0e75cdad1a0e75cc1f8f671d2c1f8f671d272cd7c855272cd7c8552c91fdd4714c91fdd47143c5ed19d673c5ed19d6749d42fc4dd49d42fc4dd1c0f8f6ecd1c0f8f6ecd5319c5e7015319c5e701f77cf6b705f77cf6b705fd6e4c71e3fd6e4c71e3be4e7d0694be4e7d069481f54903fd81f54903fd622b158b45622b158b455a6b90a0a55a6b90a0a5652ed7bac7652ed7bac7c5e6af8bf0c5e6af8bf0bd15688a7dbd15688a7d76a5ad54ca76a5ad54cac21b60668ec21b60668eb842bd4c9db842bd4c9dde40e82f13de40e82f131ee697eb4b1ee697eb4bd5faadae3cd5faadae3c05a4ef76c705a4ef76c71f1813137a1f1813137a63b16edc4663b16edc46db4bd75841db4bd758410f2023470b0f2023470bdc2bbd045ddc2bbd045db1bfa5a423b1bfa5a423b9eee989a8b9eee989a864f8b7667564f8b76675d4f5912964d4f5912964f790b4f656f790b4f656e19a294cd3e19a294cd3f234a1f176f234a1f17620b638235020b6382350addc600200addc600200c0f1502efac0f1502efac4dd38523ec4dd38523e24014af6ee24014af6ee1ba77dfdfd1ba77dfdfd8dede9bbcd8dede9bbcd8b880efea38b880efea3e78dd405bde78dd405bd9a0187e0b69a0187e0b653e4f56ff653e4f56ff6a50909b0f5a50909b0f5c965bc202dc965bc202d585b12d92c585b12d92c40a5139f1140a5139f1135495753a235495753a2227ede06a7227ede06a77ea7c421c37ea7c421c3d0baf70709d0baf707099d336a497a9d336a497ad4250db51dd4250db51dfdbd09f321fdbd09f321a197379f91a197379f9103a2a3f5a603a2a3f5a659fb28597dView command line instructions
Checkout
From your project repository, check out a new branch and test the changes.