-
-
Notifications
You must be signed in to change notification settings - Fork 73
Add pycares 5.0 support with backward-compatible result types #218
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #218 +/- ##
==========================================
+ Coverage 97.69% 99.05% +1.35%
==========================================
Files 3 5 +2
Lines 564 1266 +702
Branches 38 69 +31
==========================================
+ Hits 551 1254 +703
Misses 7 7
+ Partials 6 5 -1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Tested with HomeAssistant all good |
|
If we are bumping the version to 4 I see no point in retaining backwards compatibility. We lose important data like the TTL for all query types. |
TLDR: We should change it so they get TTL, etc it but after a bridge release to make it easier for downstream to coordinate all the needed changes which should hopefully shave of a few months (or years if other libs are slow) to the migration process (HA has libs using pycares/aiodns that it does not control are will be a the mercy of the maintainers of them to get them updated). I think we should keep the 4.x API compatible while adding pycares 5.x support. Since we didn't have a hard pin on the pycares major version, users could end up with pycares 5.x installed alongside code expecting the old aiodns API. Having a bridge release (aiodns 4.x with pycares 5.x support but the same API) ensures nobody gets stuck. Home Assistant is a concrete example: we have several dependencies that use the aiodns query API. With this approach, we can release aiodns 5.x with breaking changes whenever it's ready, and Home Assistant can stay on 4.x until all downstream deps are updated. This avoids a giant coordinated merge and complex dependency bump - each dep can migrate independently on their own timeline. To give an idea of the scope of the problem, Home Assistant has 18 deps that use |
|
Thinking about it a bit more, we could introduce a new api for modern query with a new name, deprecate |
Fair enough. |
I like this less since "query" is the obvious API to use... |
I added If you have a better idea, I'm more than happy to execute on it. |
|
With this approach, I think in 5.x we could drop the old |
|
I updated the PR summary and docs to reflect that approach.
|
|
I'm happy with this now. I've tested it many places downstream. It solves all the current issues at hand. |
|
Yeah, if we want to support both APIs for a while this makes sense. What feels a bit weird is that when we drop query, there will only be query_dns, which is odd. How about having a dedicated function per query type? query_a, query_soa, query_mx ... this way the query_xxx are the new ones and the old query() remains the old one. When we remove it, it's obvious why the others are called the way they are. Thoughts? |
| return AresQueryNSResult(host=ns_data.nsdname, ttl=ttl) | ||
| if record_type == pycares.QUERY_TYPE_TXT: | ||
| txt_data = cast(pycares.TXTRecordData, record.data) | ||
| return AresQueryTXTResult(text=txt_data.data, ttl=ttl) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that pycares4 returned str and the new one does bytes.
| Migrating from aiodns 3.x | ||
| ========================= | ||
|
|
||
| aiodns 4.x introduces a new ``query_dns()`` method that returns native pycares 5.x result types. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps a link to the pycares docs where the result types are described would help?
| __all__ = ( | ||
| 'AresHostResult', | ||
| 'AresQueryAAAAResult', | ||
| # Compatibility types for pycares 4.x API |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
weird comment location? do we want to re-export the pycares return types?
| raise RuntimeError(WINDOWS_SELECTOR_ERR_MSG) | ||
| except ModuleNotFoundError as ex: | ||
| raise RuntimeError(WINDOWS_SELECTOR_ERR_MSG) from ex | ||
| # Use weak reference to avoid preventing garbage collection |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this necessary?
| self._channel.gethostbyname(host, family, cb) | ||
| ) -> asyncio.Future[AresHostResult]: | ||
| """ | ||
| Resolve hostname to addresses. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should add a deprecation warning here and remove it in the next mayor version.
The plan for 5.x is to rename Type specific methods like With query_type = user_input # "A", "MX", "TXT", etc.
result = await resolver.query_dns(host, query_type)and than its back to With type specific we get something a bit awkward like: method = getattr(resolver, f"query_{query_type.lower()}")
result = await method(host)That said, type-specific methods could be added later as convenience wrappers as well? |
That means people need to update their code twice if they want to use the new API. I find that more incovenient than the alternative.
It's a simple mapping, I don't think it would be a big deal. Having to change the code twice to use the new stuff sounds worse IMHO. |
|
Fair point on the double migration. Though with But I see the appeal of type-specific methods being a cleaner one-time migration. I'm okay either way. What do you think is the better path forward? |
|
Let's sleep on it :-) Maybe others want to weigh in too? |
I would have to agree that would be a bit awkward, I'd probably want to avoid that. It's also very easy to lose type safety if you're trying to access these functions dynamically like that. Convenience methods in addition to the general query function (like in aiohttp) would be fine though. It doesn't sound like there's a perfect solution available, but I'd be inclined to go with bdraco's proposal currently, unless any better ideas appear. One other idea I can think of (I'm not really for or against it though) is tweaking the function signature in some way. For example, if the new version used an enum instead of strings, then we can use the same function and just switch the implementation depending on whether the argument is str or enum (which could be cleanly implemented with functools.singledispatchmethod decorator). |
|
Also, it looks like the new version has lost the overloads we had for each query type. It looks like we'll need to rework the typing in pycares 5 to make that work again. |
|
pycares should probably have similar overloads for the callback here: That way it can verify the callback expects to receive the specific type it will return, like: |
What do these changes do?
Add support for pycares 5.0 while maintaining full backward compatibility with existing code.
Key changes:
aiodns/compat.pymodule with frozen dataclasses matching pycares 4.x field namesquery_dns()method returning native pycares 5.xDNSResult(with access to answer/authority/additional sections)query()method deprecated (emitsDeprecationWarning) but continues to work with pycares 4.x compatible resultsgethostbyname()now usesgetaddrinfo()internally (pycares 5 removed gethostbyname)nameserversproperty strips port suffix added by pycares 5.x for backward compatibilityAresQueryAResult,AresQueryAAAAResult,AresQueryCNAMEResult,AresQueryMXResult,AresQueryNSResult,AresQueryTXTResult,AresQuerySOAResult,AresQuerySRVResult,AresQueryNAPTRResult,AresQueryCAAResult,AresQueryPTRResult,AresHostResultMigration path
Future migration to aiodns 5.x
The temporary
query_dns()naming allows gradual migration without breaking changes:query()query_dns()query()for back compatIn aiodns 5.x,
query()will become the primary API returning native pycares 5.x types, andquery_dns()will remain as an alias for backward compatibility. This allows downstream projects to migrate at their own pace.Field mappings (pycares 5.x → aiodns compat)
data.addrhostdata.exchangehostdata.nsdnamehostdata.datatextmname/rname/expire/minimumnsname/hostmaster/expires/minttldata.targethostdata.tagpropertydata.dnamenameAre there changes in behavior for the user?
Backward compatible - existing code continues to work unchanged (with deprecation warning on
query()).Version bumped to 4.0.0 due to:
query()are now aiodns dataclasses instead of pycares typesThe
query()type change is unlikely to affect anyone since checkingisinstance(result, pycares.ares_query_*_result)was uncommon. Field access patterns remain identical to pycares 4.x.Users can migrate to
query_dns()at their own pace while staying on aiodns 4.x, avoiding coordinated dependency upgrades.Related issue number
Fixes #214
Checklist