Skip to content

Add Deprecated APIs Depended on by Legacy Tools #70

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

Open
wants to merge 30 commits into
base: master
Choose a base branch
from

Conversation

biniona
Copy link
Contributor

@biniona biniona commented Apr 10, 2025

This PR is essentially a revert of the following four commits, being careful to only re-add features that are currently depended on:

This PR adds the following deprecated APIs to allow the Public version of this project to fully replace the private version.

The depreacted APIs we are adding are as follows:

  • Initialize classes specified in the registry.autostart namespace in a config file
  • Adding the _name and _start parameters back into the inject.bind() + inject.define() functions, allowing you to rename a binding in the registry and define a method to call when the binding is first initialized.
  • Adding Registry.start back in
  • Adding start_method + close_method decorators back in
  • Adding the registry.config.from_dict method back in

NONE OF THESE APIS SHOULD BE USED BY ANY NEW CODE!!!!

I am not adding the following deprecated APIs back in as they do not appear to be used:

  • inject.name
  • config.RegistrySubConfig
  • config.InternalRegistryConfig
  • Initializing classes using the registry.by_class and registry.by_name namespaces

Semi-Related Change - I am also making the RegistryWrapper class private. It is not referenced outside the library and as far as I can tell it has no reason to be. This also allows us to not mark aspects of the class as deprecated, as it is private.

@biniona biniona mentioned this pull request Apr 10, 2025
@biniona biniona changed the title Add Deprecated APIs Depended on by Core Library Add Deprecated APIs Depended on Other Libraries Apr 10, 2025
@biniona biniona changed the title Add Deprecated APIs Depended on Other Libraries Add Deprecated APIs Depended on by Legacy Tools Apr 10, 2025

.. deprecated:: 1.0
The _name and _start parameters are deprecated and should not be used in new code.
To replace _start, use a context manager instead. There is no replacement for _name.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I plan to add the context manager API as the next PR

return getattr(module, var_name)


class _RegistryWrapper(Generic[T]):
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I made this private for the following reasons:

  • It always should have been private - and it is not currently depended on
  • It lets us not mark the start/close methods as deprecated

@biniona biniona marked this pull request as ready for review April 10, 2025 21:50
@biniona biniona requested a review from bcmills April 10, 2025 21:50
"""Configure the registry from a dictionary-like mapping.
The provided mapping should contain general configuration that can
be accessed using the inject.config decorator.

.. deprecated:: 1.0
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not familiar with this pydoc syntax, and having some trouble finding a description of it — could you send me a link to the doc?

Copy link
Contributor Author

@biniona biniona Apr 11, 2025

Choose a reason for hiding this comment

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

I believe this is a restructured text admonition - which could be rendered using a tool like Sphinx.

I am not aware of a standard way to deprecate Python code for Python<3.13 (@deprecated) - so Claude's suggestion to use this seemed alright to me.

The only test I did for this was checking how it rendered when hovering over the function/method name in VSCode:

Screenshot 2025-04-11 at 1 33 38 PM

Let me know if this seems incorrect!


# we must export this from minject to provide backwards
# compatability to a legacy system
__all__ = ["_get_meta"]
Copy link
Contributor

Choose a reason for hiding this comment

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

If I understand PEP-8 correctly, __all__ should include all of the public identifiers, not just exceptional additions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added all public methods in the inject module to this all` list.

"""
for key in self._autostart_candidates():
LOG.debug("autostarting %s", key)
self[key] # pylint: disable=pointless-statement
Copy link
Contributor

Choose a reason for hiding this comment

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

(nit) I think _ = self[key] would be a cleaner way to disable this warning?

@@ -115,9 +128,41 @@ async def _push_async_context(self, key: Any) -> Any:
).strip()
)

def _autostart_candidates(self) -> Iterable[RegistryKey]:
registry_config: Mapping[str, Any] = self.config.get("registry")
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be Optional[Mapping[str, Any]]? The if registry_config: below suggests that this could be None — if it were merely an empty Mapping, that condition would be redundant.

Perhaps:

        registry_config: Optional[Mapping[str, Any]] = self.config.get("registry")
        if registry_config is not None:
            if (autostart := registry_config.get("autostart")) is not None:
                return (_resolve_import(value) for value in autostart)
        return ()

self._by_meta: Dict[RegistryMetadata, RegistryWrapper] = {}
self._by_name: Dict[str, RegistryWrapper] = {}
self._by_iface: Dict[type, List[RegistryWrapper]] = {}
self._objects: List[_RegistryWrapper] = []
Copy link
Contributor

Choose a reason for hiding this comment

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

_RegistryWrapper is parameterized on T, so these fields should use _RegistryWrapper[Any] instead of just _RegistryWrapper. (I fixed these in the internal repo in https://github.com/duolingo/python-duolingo-base/pull/667 — you might be able to have Cursor backport that patch.)


This also suggests that we need to improve the type-checking in this repo's CI configuration!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great points. I'll do the following:

  • Fix this type error
  • Port #667 to this PR where applicable
  • Update mypy check on this repo to run --strict

Let me know if you have any concerns with this approach

@@ -476,6 +495,20 @@ def lazy_load_object(i):
for count in Counter(map(id, results)).values():
self.assertEqual(count, query_per_class)

def test_bind_name_parameter(self) -> None:
"""Test that the _name parameter of inject.bind() works correctly and emits a deprecation warning."""
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this is checking the “and emits a deprecation warning” part?

@@ -96,12 +123,49 @@ def async_context(cls: Type[T_async_context]) -> Type[T_async_context]:
return cls


def start_method(cls, method):
# type: (Type[T], Callable[[T], None]) -> None
"""Function to bind a registry start function for a class.
Copy link
Contributor

Choose a reason for hiding this comment

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

A bit more detail in the documentation here would be helpful — if there is already a start function bound, what is the intended behavior? I can think of three reasonable-ish options:

  1. The call to start_method throws something like an AssertionError or ValueError, indicating erroneous usage.
  2. The new method is registered to be called after any previously-registered methods.
  3. The new method replaces any previously-registered methods. (But then we probably also need some API for reporting the previous methods, so that they can be chained properly?)

The implementation here seems to be (3), but I don't see an API or examples for how do properly chain calls. 🤔

Would it be feasible to switch to (1) instead, or do we think that will break existing usage?

Copy link
Contributor Author

@biniona biniona Apr 11, 2025

Choose a reason for hiding this comment

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

Gread idea. I checked the start_method usage and AFAICT it is not paired with a @<decorator>(_start=...) usage.

We should be able to update the API to 1.

Added a test to check this as well.

@biniona
Copy link
Contributor Author

biniona commented Apr 11, 2025

Waiting on the final go ahead from @mmchenry-duolingo to merge

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants