base.py 1.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
  1. from __future__ import annotations
  2. import abc
  3. import logging
  4. import typing as t
  5. from pathlib import Path
  6. from findpython.python import PythonVersion
  7. from findpython.utils import path_is_python, safe_iter_dir
  8. logger = logging.getLogger("findpython")
  9. class BaseProvider(metaclass=abc.ABCMeta):
  10. """The base class for python providers"""
  11. version_maker: t.Callable[..., PythonVersion] = PythonVersion
  12. @classmethod
  13. def name(cls) -> str:
  14. """Configuration name for this provider.
  15. By default, the lowercase class name with 'provider' removed.
  16. """
  17. self_name = cls.__name__.lower()
  18. if self_name.endswith("provider"):
  19. self_name = self_name[: -len("provider")]
  20. return self_name
  21. @classmethod
  22. @abc.abstractmethod
  23. def create(cls) -> t.Self | None:
  24. """Return an instance of the provider or None if it is not available"""
  25. pass
  26. @abc.abstractmethod
  27. def find_pythons(self) -> t.Iterable[PythonVersion]:
  28. """Return the python versions found by the provider"""
  29. pass
  30. @classmethod
  31. def find_pythons_from_path(
  32. cls, path: Path, as_interpreter: bool = False
  33. ) -> t.Iterable[PythonVersion]:
  34. """A general helper method to return pythons under a given path.
  35. :param path: The path to search for pythons
  36. :param as_interpreter: Use the path as the interpreter path.
  37. If the pythons might be a wrapper script, don't set this to True.
  38. :returns: An iterable of PythonVersion objects
  39. """
  40. return (
  41. cls.version_maker(
  42. child.absolute(),
  43. _interpreter=child.absolute() if as_interpreter else None,
  44. )
  45. for child in safe_iter_dir(path)
  46. if path_is_python(child)
  47. )