Находки в опенсорсе: Python
1.06K subscribers
5 photos
348 links
Легкие задачки в опенсорсе из мира Python

Чат: @opensource_findings_chat
Download Telegram
🚀 New issue to ag2ai/faststream by @Lancetnik
📝 Bug: `StreamSub` warns that `no_ack` has no effect with a consumer group, but the flag is forwarded to XREADGROUP NOACK (#3126)


Describe the bug

StreamSub(..., group=..., consumer=..., no_ack=True) emits

RuntimeWarning: `no_ack` has no effect with consumer group

but the flag does have an effect: it reaches Redis as XREADGROUP ... NOACK, so entries never enter the PEL. The warning is a leftover from a time when the flag was not forwarded.

How to reproduce

from faststream.redis import RedisBroker, StreamSub

broker = RedisBroker()


@broker.subscriber(
stream=StreamSub("my-stream", group="my-group", consumer="c1", no_ack=True),
)
async def handler(msg: str) -> None:
...

Constructing the StreamSub prints the warning. Running the app shows that the flag is honoured anyway: XPENDING my-stream my-group stays empty after messages are consumed.

Expected behavior

No warning. no_ack=True with a consumer group is a supported, documented mode ("equivalent to acknowledging the message when it is read", see docs/docs/en/redis/streams/groups.md).

Observed behavior

The warning is emitted in faststream/redis/schemas/stream_sub.py (the elif no_ack: branch under if group and consumer:), while the rest of the code base treats the flag as live:

• faststream/redis/subscriber/usecases/stream_subscriber.py passes noack=stream.no_ack to XREADGROUP;
• faststream/redis/subscriber/config.py switches the subscriber to AckPolicy.MANUAL when no_ack is set;
• faststream/redis/testing.py skips the PEL for no_ack subscribers;
• #3049 added a SetupError for claim_min_idle_time + no_ack, which only makes sense if no_ack works with groups.

History

• The warning was introduced in 0.3.5 (#1048), when no_ack was indeed not forwarded to XREADGROUP.
• #2309 kept the warning and reset no_ack = False right after it, so the flag really was ignored for a while.
• 0.6.0 (#1779) dropped that reset, and noack=stream.no_ack has been sent to Redis since, but the warning stayed.

The sibling warning, "no_ack is not supported by consumer group with last_id other than >", is a separate case and should be checked on its own: Redis documents NOACK for XREADGROUP regardless of the id, so it may be stale too.

Suggested fix

Remove the elif no_ack: warning branch in StreamSub.__init__ and add a test asserting that StreamSub("s", group="g", consumer="c", no_ack=True) raises no warning and that the subscriber calls xreadgroup with noack=True. Keep the claim_min_idle_time + no_ack SetupError as is.

Environment

Reproduced on main (commit 0307f09).


#bug #good_first_issue #redis #faststream #ag2ai
sent via relator
🚀 New issue to wemake-services/django-modern-rest by @sobolevn
📝 Make sure that `SecuritySchemeRegistry` can't re-register existing auth schemes (#1488)


Currently SecuritySchemeRegistry does not protect from registerting two auth schemes with the same name:

django-modern-rest/dmr/openapi/core/registry.py

Lines 127 to 142 in f822e35

This needs to be fixed. It must raise an error, no error must pass silently.


#bug #good_first_issue #help_wanted #openapi #opensource_september #django_modern_rest
sent via relator
🚀 New issue to wemake-services/django-modern-rest by @sobolevn
📝 Support custom `format` options, add more pre-defined formats (#1489)


Currently we only allow format strings that are present in OpenAPIFormat enum. Which is not correct, because format is an open str type. Users can create custom formats. See the spec: https://spec.openapis.org/oas/v3.2.0.html#data-type-format

Also, add all formats from https://spec.openapis.org/registry/format/ to OpenAPIFormat just for the usability.

Also, please make sure that these types work correctly in tests/test_unit/test_plugins/test_pydantic/test_pydantic_snapshots.py:

• pydantic.NameEmail
• pydantic.Base64Str
• pydantic.Base64Bytes
• Custom format with json_schema_extra={'custom-format': ...} for regular str field in pydantic

#bug #good_first_issue #help_wanted #openapi #opensource_september #django_modern_rest
sent via relator
🚀 New issue to wemake-services/django-modern-rest by @sobolevn
📝 Fix missing `Schema` attributes in `load_schema` (#1490)


In my personal opinion load_schema is the ugliest part of the framework. New issues proposing better design of this feature is MASSIVELY welcome. But, before that we need to fix the existing problem.

Here's how a schema is loaded right now:

django-modern-rest/dmr/openapi/mappers/schema_loader.py

Lines 67 to 127 in f822e35

We never set: anchor, comment, ref, and schema_uri. Maybe others?

But, $ref is the most complex one here. Please, ignore it for now, we must fix our Reference | Schema handling in all places at once 😢


#bug #good_first_issue #help_wanted #openapi #opensource_september #django_modern_rest
sent via relator
🚀 New issue to wemake-services/django-modern-rest by @sobolevn
📝 Fix `Reference | Schema` conflict in our OpenAPI definitions (#1491)


Currently we mix up two different things in the code: Schema with $ref set and a Reference object with $ref set.

For example, this code:

import pydantic
from pydantic.json_schema import GenerateJsonSchema
from dmr.openapi.mappers.schema_loader import load_schema


class WithDialect(GenerateJsonSchema):
def generate(self, schema, mode='validation'):
json_schema = super().generate(schema, mode=mode)
json_schema['$schema'] = self.schema_dialect
return json_schema


class Address(pydantic.BaseModel):
model_config = pydantic.ConfigDict(
json_schema_extra={'$anchor': 'address', '$comment': 'Postal address'},
)
city: str


class User(pydantic.BaseModel):
model_config = pydantic.ConfigDict(
json_schema_extra={'$comment': 'Internal note for schema readers'},
)
name: str = pydantic.Field(json_schema_extra={'$comment': 'Display name'})
address: Address = pydantic.Field(
default=Address(city='Moscow'),
description='Where the user lives',
)


raw = pydantic.TypeAdapter(User).json_schema(
ref_template='#/components/schemas/{model}',
schema_generator=WithDialect,
)
defs = raw.pop('$defs')

Produces this schema:

{
"$comment": "Internal note for schema readers",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"name": {"$comment": "Display name", "title": "Name", "type": "string"},
"address": {
"$ref": "#/components/schemas/Address",
"default": {"city": "Moscow"},
"description": "Where the user lives"
}
},
"required": ["name"], "title": "User", "type": "object"
}

But, we would load it as Reference object in load_schema and we will loose default and description. Which are Schema object attributes.

So, what we need to do?

1. Analyze all places where Reference can't be even used based on https://spec.openapis.org/oas/v3.2.0.html We need to grep this page with | Reference Object and check that we don't have more places that can hold Reference objects
2. We must distinguish $ref in potential Schema objects and Reference objects
3. We must change how our maybe_resolve_reference works to also resolve $ref in Schema objects, where we need it. Maybe we create a new type ResolvedSchema and use it in places where we expect flat schemas, so we won't forget to call maybe_resolve_reference

#bug #good_first_issue #help_wanted #openapi #opensource_september #django_modern_rest
sent via relator
🚀 New issue to wemake-services/django-modern-rest by @sobolevn
📝 Change how we work with `FileMetadata` validation (#1493)


Currently, this code is allowed:

from typing import Literal

import pydantic

from dmr import Body, Controller, FileMetadata
from dmr.plugins.pydantic import PydanticSerializer
from dmr.parsers import JsonParser, MultiPartParser


class Text(pydantic.BaseModel):
content_type: Literal['text/plain']


class Files(pydantic.BaseModel):
text: Text


class MyController(Controller[PydanticSerializer]):
parsers = [JsonParser(), MultiPartParser()]

def post(
self,
parsed_body: Body[dict[str, str]],
parsed_file_metadata: FileMetadata[Files],
) -> str:
return 'done'

But, how can we ever satisfy?

• JsonParser()
• parsed_file_metadata: FileMetadata[Files]

It is not optional. Every request here will fail.
What we can do? Change

django-modern-rest/dmr/components.py

Lines 866 to 889 in f822e35

from any to all.


#bug #good_first_issue #help_wanted #opensource_september #django_modern_rest
sent via relator
🚀 New issue to wemake-services/django-modern-rest by @sobolevn
📝 It is possible to produce an empty `description` for `ResponseBody` (#1495)


django-modern-rest/dmr/openapi/generators/component_parsers.py

Lines 193 to 199 in f822e35

It misses or None part. Because we only skip None values from dumping.


#bug #good_first_issue #help_wanted #openapi #opensource_september #django_modern_rest
sent via relator
🚀 New issue to wemake-services/django-modern-rest by @sobolevn
📝 Test explicit `OpenAPIConfig.security` definition with `auth=None` per-endpoint (#1497)


Looks like we have a potential bug in how we define security in OpenAPI.
Here we always return None:

django-modern-rest/dmr/openapi/generators/security_scheme.py

Lines 24 to 37 in f822e35

But, if per-document security is set, we need to return [] as the value. Otherwise, None won't be dumped by our None-ignoring dump_schema rules. And security rules per this endpoint will be inherited from the document.

Which is wrong.

This would require a test and a fix. Maybe we should look at current config's security to define the return type?


#bug #good_first_issue #help_wanted #openapi #opensource_september #django_modern_rest
sent via relator
🚀 New issue to wemake-services/django-modern-rest by @sobolevn
📝 Fix unused `security` field in `EndpointMetadata` and `_BasePayload` (#1499)


We don't use these two things:

1. security field documentation here:

 django-modern-rest/dmr/metadata.py

 Lines 429 to 430 in f822e35

2. django-modern-rest/dmr/validation/payload.py

 Line 44 in f822e35

However, I don't think that this is right. Users must have an option to configure this directly.
Let's create this rule:

• Any user provided security objects must be saved to the metadata
• It must be merged with explicit auth= providers

It might be useful when people define some external security mechanims, for example that work on HTTP proxy level, or in other microservices.

New snapshot test is required for this change.


#bug #good_first_issue #help_wanted #openapi #opensource_september #django_modern_rest
sent via relator
🚀 New issue to wemake-services/django-modern-rest by @sobolevn
📝 Fix `operation_id` default generation rules (#1500)


Currently it produces: getAuthedandcookiescontrollerApiCookies for _AuthedAndCookiesController, see

django-modern-rest/tests/test_unit/test_plugins/test_msgspec/test_msgspec_snapshots.py

Lines 61 to 81 in f822e35

It must produce: getAuthedAndCookiesControllerApiCookies
Please, update all existing snapshots.


#bug #good_first_issue #help_wanted #openapi #opensource_september #django_modern_rest
sent via relator
🚀 New issue to wemake-services/django-modern-rest by @sobolevn
📝 `test_custom_union_format` failed in CI (#1533)


Seed for the repro: --randomly-seed=1177358910

FAILED tests/test_unit/test_plugins/test_pydantic/test_pydantic_schema.py::test_custom_union_format[PydanticSerializer] - AssertionError: assert Schema(all_of...ne, defs=None) == Schema(all_of...ne, defs=None)

Omitting 58 identical items, use -vv to show
Differing attributes:
['type']

Drill down into differing attribute type:
type: [<OpenAPIType.STRING: 'string'>, <OpenAPIType.INTEGER: 'integer'>] != [<OpenAPIType.INTEGER: 'integer'>, <OpenAPIType.STRING: 'string'>]
At index 0 diff: <OpenAPIType.STRING: 'string'> != <OpenAPIType.INTEGER: 'integer'>

Full diff:
[
+ <OpenAPIType.STRING: 'string'>,
<OpenAPIType.INTEGER: 'integer'>,
- <OpenAPIType.STRING: 'string'>,
]
FAILED tests/test_unit/test_plugins/test_pydantic/test_pydantic_schema.py::test_custom_union_format[PydanticFastSerializer] - AssertionError: assert Schema(all_of...ne, defs=None) == Schema(all_of...ne, defs=None)

Omitting 58 identical items, use -vv to show
Differing attributes:
['type']

Drill down into differing attribute type:
type: [<OpenAPIType.STRING: 'string'>, <OpenAPIType.INTEGER: 'integer'>] != [<OpenAPIType.INTEGER: 'integer'>, <OpenAPIType.STRING: 'string'>]
At index 0 diff: <OpenAPIType.STRING: 'string'> != <OpenAPIType.INTEGER: 'integer'>

Full diff:
[
+ <OpenAPIType.STRING: 'string'>,
<OpenAPIType.INTEGER: 'integer'>,
- <OpenAPIType.STRING: 'string'>,
]

https://github.com/wemake-services/django-modern-rest/actions/runs/34912494319/job/104202853421?pr=1510


#bug #help_wanted #ci #opensource_september #django_modern_rest
sent via relator
🚀 New issue to wemake-services/django-modern-rest by @sobolevn
📝 `CookieJWT[A]syncAuth` and `HeaderJWT[A]syncAuth` have the same security schema name (#1587)


This makes it impossible to use them together in a single endpoint / contoller together by default. Only a single jwt security schema / requirement will be generated.

Which is not correct. We need to change the CookieJWT[A]syncAuth one to be jwt_cookie


#bug #good_first_issue #help_wanted #openapi #opensource_september #django_modern_rest
sent via relator
🚀 New issue to wemake-services/django-modern-rest by @sobolevn
📝 Flaky `test_empty_validate_responses` (#1588)


 ________________________ test_empty_validate_responses _________________________

settings = <pytest_django.fixtures.Settings object at 0x7f00e6dbba90>

def test_empty_validate_responses(settings: LazySettings) -> None:
"""Ensure that `EMPTY` on `validate_responses` raises."""
settings.DMR_SETTINGS = {
Settings.validate_responses: EMPTY,
}

> with pytest.raises(EndpointMetadataError, match='validate_responses'):
E AssertionError: Regex pattern did not match.
E Expected regex: 'validate_responses'
E Actual message: 'Settings validation failed'


Link: https://github.com/wemake-services/django-modern-rest/actions/runs/36040317321/job/107770717079

The fix is very welcome!


#bug #good_first_issue #help_wanted #opensource_september #django_modern_rest
sent via relator
🚀 New issue to wemake-services/wemake-python-styleguide by @sobolevn
📝 Incorrect Jones complexity for formatted strings (#3820)


These two lines:

• tuple(f'/{pref.strip("/")}/' for pref in (prefix, *prefixes))
• tuple(f'/{pref.strip("/")}' for pref in (prefix, *prefixes)) (notice the missing / at the end)

have different Jones complexity.
They must not. It should be counted as:

• 1 per every {}
• As regular for all python expressions inside {}
• All string parts of fstring are counted as total of 1

#bug #help_wanted #levelstarter #good_first_issue #wemake_python_styleguide #wps
sent via relator
🚀 New issue to wemake-services/django-modern-rest by @vyhuholl
📝 Seeded OpenAPI examples of `datetime` and `time` change with the current time (#1632)


What's wrong?

Found while working on #1629. With DMR_SETTINGS = {'openapi_examples_seed': 1} and the same PYTHONHASHSEED, examples of datetime and time fields are different on every run:

class _Event(msgspec.Struct):
created_at: dt.datetime
day: dt.date
at: dt.time
duration: dt.timedelta


class _EventController(Controller[MsgspecSerializer]):
def get(self) -> _Event:
raise NotImplementedError

Two runs 3 seconds apart, both with PYTHONHASHSEED=0:

02:18:43  {"created_at": "2000-10-08T03:39:12.378838", "day": "2025-09-16", "at": "18:36:00.366279", "duration": "P0D"}
02:18:46 {"created_at": "2000-10-08T03:39:16.378838", "day": "2025-09-16", "at": "18:36:03.421377", "duration": "P0D"}

polyfactory generates these with Faker, and Faker's defaults end at the current time: datetime uses date_time_between, which ends at "now", and time uses time_object, which picks a time between 1970 and now. The seed fixes the random part, but the range moves with the clock, so the value moves too. date uses date_this_decade, which ends at today, so it changes once a day.

So a schema with such fields is never the same on two runs, even though the seed_example_factory docstring says that seeding per build "keeps a schema reproducible on its own".

How it should be?

The same openapi_examples_seed should give the same examples at any time. _ExampleFactory in dmr/openapi/mappers/example.py can override get_provider_map() and give datetime, date and time fixed bounds instead of "now".

timedelta could get a range there too: its examples are always P0D now, because time_delta() without end_datetime starts and ends at "now".

Used versions

django-modern-rest 0.15.0, polyfactory 3.3.0, faker 40.39.0

Reproduced on c35128a.

OS information

macOS 27.0


#bug #good_first_issue #help_wanted #openapi #opensource_september #django_modern_rest
sent via relator
🚀 New issue to wemake-services/wemake-python-styleguide by @sobolevn
📝 `WPS235`: ignore imports from `typing` (#3824)


I can't limit the number of types I import from typing, I can't refactor types to use typing. prefix and import import typing

We need to just ignore this module. And typing_extensions as well.


#bug #help_wanted #levelstarter #good_first_issue #wemake_python_styleguide #wps
sent via relator
🚀 New issue to wemake-services/django-modern-rest by @Izcarmt95
📝 OpenAPI: duplicated tags when both Router(tags=...) and @modify(tags=...) set the same tag (#1656)


When an operation has a tag in @modify(tags=...) and its Router has the same tag in Router(tags=...), the generated OpenAPI operation lists the tag twice. UIs like Scalar then show every operation twice, once per tag entry.

from dmr import Controller, modify
from dmr.openapi import build_schema
from dmr.plugins.pydantic import PydanticSerializer
from dmr.routing import Router, path


class UserController(Controller[PydanticSerializer]):
@modify(tags=['Users'])
def get(self) -> str:
return 'ok'


users = Router('/users', [path('/all', UserController.as_view(), name='users')], tags=['Users'])
api = Router('api')
api.include(users, namespace='users')

schema = build_schema(api)
print(schema.paths['/api/users/all'].get.tags) # ['Users', 'Users']

Expected: ['Users']. The router tags and the endpoint tags should be merged without duplicates.

This looks related to the change for #1299, which now propagates router metadata to included routes.

• django-modern-rest 0.15.0
• Django 5.2, Python 3.13
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯

AI disclaimer: an AI assistant was used only to help write this issue.


#bug #good_first_issue #help_wanted #openapi #opensource_september #django_modern_rest
sent via relator
🚀 New issue to wemake-services/django-modern-rest by @Izcarmt95
📝 Router.include() raises KeyError for a route with an empty pattern ('') (#1657)


Router.include() raises KeyError when the included router has a route with an empty pattern (path('', ...)), which is the usual way to mount a list endpoint at the router's prefix.

from dmr import Controller
from dmr.plugins.pydantic import PydanticSerializer
from dmr.routing import Router, path


class UserListController(Controller[PydanticSerializer]):
def get(self) -> str:
return 'ok'


users = Router('/users', [path('', UserListController.as_view(), name='users')])
api = Router('api')
api.include(users, namespace='users') # KeyError: '/users'

  File ".../dmr/routing.py", line 173, in include
router.metadata_for(original_path),
File ".../dmr/routing.py", line 217, in metadata_for
return self._path_metadata[pattern]
KeyError: '/users'

Router.__init__ stores the metadata of the empty route under '/users/' (normalized with original_prefix=''), but include() looks it up as '/users' (normalized with original_prefix=router.prefix). Routes with a non-empty pattern (path('/<id>', ...)) are fine.

Workaround: use an empty router prefix and write the full segment in each route (Router('', [path('/users', ...)])).

• django-modern-rest 0.15.0 (it worked in 0.13.0, before include() looked up the metadata)
• Django 5.2, Python 3.13
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯

AI disclaimer: an AI assistant was used only to help write this issue.


#bug #good_first_issue #help_wanted #opensource_september #django_modern_rest
sent via relator
🚀 New issue to wemake-services/django-modern-rest by @sobolevn
📝 Set `security=None` for concrete login views (#1719)


We already set auth=None for them, but security= must also be disabled for the consistency.


#bug #good_first_issue #help_wanted #openapi #django_modern_rest
sent via relator
❤1
🚀 New issue to wemake-services/django-modern-rest by @joefefs
📝 None endpoints still document a JSON null response body. (#1733)


What's wrong?

For endpoints that returns nothing, the server correctly sends an empty body, but the schema describes that response as application/json with a null schema.

Anything that trusts that schema (like an SDK generator) might try to read this null body as JSON and crash.

How it should be?

The response should NOT have a content key, only a { "description": "No Content" } should suffice so that clients relying on the spec won't try to read a body that does not exist.

Used versions
• django-modern-rest 0.16.0
• Django 5.2.17
• Python 3.13.12
• pydantic 2.13.5
• msgspec 0.22.0

OS information

macOS 26.3.2


#bug #good_first_issue #help_wanted #openapi #django_modern_rest
sent via relator