8000 make counter classes public by vittoriopolverino · Pull Request #12386 · localstack/localstack · GitHub
[go: up one dir, main page]

Skip to content

make counter classes public #12386

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

Merged
merged 4 commits into from
Mar 14, 2025
Merged

make counter classes public #12386

merged 4 commits into from
Mar 14, 2025

Conversation

vittoriopolverino
Copy link
Member
@vittoriopolverino vittoriopolverino commented Mar 13, 2025

Motivation

@bentsku noticed this behavior while working on this PR, thanks for the heads-up! 🚀
Before this change, the classes _CounterMetric and _LabeledCounterMetric were "protected", which made it inappropriate to use them directly for static typing. This caused type hinting issues, particularly in cases where a Counter instance was assigned to a class attribute:

class TestClass:
    counter: Counter

    def __init__(self, counter: Counter):
        self.counter = counter or Counter(name="test", labels=["l1"])

        self.counter.labels(l1="test_value").increment()

Screenshot 2025-03-13 at 15 38 49

Changes

The CounterMetric and LabeledCounterMetric classes have been made public, allowing clients to use them directly for typing. The use of TypeVar and Protocol was also considered, but it may not be necessary to rely on them in this specific case.

class TestClass:
    counter: LabeledCounterMetric

    def __init__(self, counter: LabeledCounterMetric):
        self.counter = counter or Counter(name="test", labels=["l1"])

        self.counter.labels(l1="test_value").increment()

@vittoriopolverino vittoriopolverino self-assigned this Mar 13, 2025
@vittoriopolverino vittoriopolverino added the semver: patch Non-breaking changes which can be included in patch releases label Mar 13, 2025
@vittoriopolverino vittoriopolverino added this to the 4.3 milestone Mar 13, 2025
Copy link
Member
@alexrashed alexrashed left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Type hints are an important point of devx! Thanks for addressing this! Since all current usages are via the Counter init, and the types are not yet used, this change should be safe 🚀

@@ -136,7 +136,7 @@ def reset(self) -> None:
self._count = 0


class _CounterMetric(Metric, _Counter):
class CounterMetric(Metric, _Counter):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question:_Counter is still private as to the convention with the leading underscore. Would that mean that increment and reset are still considered private functions in the IDE integration / for type suggestions?

Copy link
Member Author
@vittoriopolverino vittoriopolverino Mar 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The IDE suggestions seem to work the same as before, showing both increment and reset as public methods. It does feel odd to see a public method suggested from a class that is, by convention, private.

Making _Counter public would require renaming it to avoid conflicts with the factory class, and it would also implicitly allow direct usage, something I’d like to avoid.

Would making it public while explicitly stating in the docstring that it should not be used directly be an acceptable solution? It seems like the Counter factory class (Counter) is creating more ambiguity than value 🤔

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, that's totally yours to judge. As mentioned in the initial PR on the new metrics framework, I don't really see the need for the Counter class which "redirects to the right class" in the first place, but it's fair if you want to keep it this way. Also, as mentioned in the other comment, you can very much guide the usage of this framework, and - together with comments in the docstring - can drive the usage. Once it's used in a specific way all over the place it will be used in this way moving forward for new implementations as well usually.

If you really want to keep it like that you can still make the interface explicit and abstract while having a private class with the actual implementation (as outlined in my other comment).
That would bloat up the amount of classes, but would allow you to have everything you want:

  • Create public abstract base classes defining the interfaces for both the CounterMetric, the Counter, and the LabeledCounterMetric.
  • Create private implementations of each of the abstract classes.
  • Use the public abstract classes as type hints for the Counter "factory", but the private implementations when creating them.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, agreed. In a separate PR over the next few days, I’ll either remove the factory class or implement abstract classes to define the interfaces. In the meantime, I’m adding comments in the docstrings to avoid blocking the other incoming PRs. This upcoming change won’t impact how the data is sent.

Additionally, introducing that change should be fairly straightforward for me and won’t require involving other engineers, as they’re already handling the migration from the old counter to the new one.

Thanks again for the feedback! 💪🏼 🙏🏼

@@ -136,7 +136,7 @@ def reset(self) -> None:
self._count = 0


class _CounterMetric(Metric, _Counter):
class CounterMetric(Metric, _Counter):
Copy link
Member
8000

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: ‏Making them public could mean that they are used directly instead of via the Counter init, Is that something that is fine, or should this be mentioned explicitly in the docstring maybe?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, this is something I’d like to avoid (having the classes used directly instead of the Counter). Do you think specifying this in the docstring would be sufficient?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That depends, you can make it absolutely clear by creating abstract classes, where the actual implementations are private, but I don't think that would be necessary. You can add it to the docstring, and you can guide core engineers in the PRs when they migrate to the new metrics client. I think that should be enough for now. :)

Co-authored-by: Alexander Rashed <2796604+alexrashed@users.noreply.github.com>
Copy link
github-actions bot commented Mar 13, 2025

LocalStack Community integration with Pro

    2 files  ±  0      2 suites  ±0   1h 52m 20s ⏱️ -9s
4 288 tests +144  3 968 ✅ +144  320 💤 ±0  0 ❌ ±0 
4 290 runs  +144  3 968 ✅ +144  322 💤 ±0  0 ❌ ±0 

Results for commit 68e55e4. ± Comparison against base commit 20f989b.

♻️ This comment has been updated with latest results.

@vittoriopolverino vittoriopolverino merged commit d1678b1 into master Mar 14, 2025
35 checks passed
@vittoriopolverino vittoriopolverino deleted the make-counters-public branch March 14, 2025 12:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
semver: patch Non-breaking changes which can be included in patch releases
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants
0