feat: add AppHandler methods, get_distribution() and get_version()
This commit is contained in:
parent
0a46dddf3f
commit
2a21e70ff1
|
@ -152,6 +152,90 @@ class AppHandler:
|
|||
return self.config.get(f'{self.appname}.app_title',
|
||||
default=default or self.default_app_title)
|
||||
|
||||
def get_distribution(self, obj=None):
|
||||
"""
|
||||
Returns the appropriate Python distribution name.
|
||||
|
||||
If ``obj`` is specified, this will attempt to locate the
|
||||
distribution based on the top-level module which contains the
|
||||
object's type/class.
|
||||
|
||||
If ``obj`` is *not* specified, this behaves a bit differently.
|
||||
It first will look for a :term:`config setting` named
|
||||
``wutta.app_dist`` (or similar, dpending on :attr:`appname`).
|
||||
If there is such a config value, it is returned. Otherwise
|
||||
the "auto-locate" logic described above happens, but using
|
||||
``self`` instead of ``obj``.
|
||||
|
||||
In other words by default this returns the distribution to
|
||||
which the running :term:`app handler` belongs.
|
||||
|
||||
See also :meth:`get_version()`.
|
||||
|
||||
:param obj: Any object which may be used as a clue to locate
|
||||
the appropriate distribution.
|
||||
|
||||
:returns: string, or ``None``
|
||||
|
||||
Also note that a *distribution* name is different from a
|
||||
*package* name. The distribution name is how things appear on
|
||||
PyPI for instance.
|
||||
|
||||
If you want to override the default distribution name (and
|
||||
skip the auto-locate based on app handler) then you can define
|
||||
it in config:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[wutta]
|
||||
app_dist = My-Poser-Dist
|
||||
"""
|
||||
if obj is None:
|
||||
print(self.appname)
|
||||
dist = self.config.get(f'{self.appname}.app_dist')
|
||||
if dist:
|
||||
return dist
|
||||
|
||||
# TODO: do we need a config setting for app_package ?
|
||||
#modpath = self.config.get(f'{self.appname}.app_package')
|
||||
modpath = None
|
||||
if not modpath:
|
||||
modpath = type(obj if obj is not None else self).__module__
|
||||
pkgname = modpath.split('.')[0]
|
||||
|
||||
try:
|
||||
from importlib.metadata import packages_distributions
|
||||
except ImportError: # python < 3.10
|
||||
from importlib_metadata import packages_distributions
|
||||
|
||||
pkgmap = packages_distributions()
|
||||
if pkgname in pkgmap:
|
||||
dist = pkgmap[pkgname][0]
|
||||
return dist
|
||||
|
||||
# fall back to configured dist, if obj lookup failed
|
||||
if obj is not None:
|
||||
return self.config.get(f'{self.appname}.app_dist')
|
||||
|
||||
def get_version(self, dist=None, obj=None):
|
||||
"""
|
||||
Returns the version of a given Python distribution.
|
||||
|
||||
If ``dist`` is not specified, calls :meth:`get_distribution()`
|
||||
to get it. (It passes ``obj`` along for this).
|
||||
|
||||
So by default this will return the version of whichever
|
||||
distribution owns the running :term:`app handler`.
|
||||
|
||||
:returns: Version as string.
|
||||
"""
|
||||
from importlib.metadata import version
|
||||
|
||||
if not dist:
|
||||
dist = self.get_distribution(obj=obj)
|
||||
if dist:
|
||||
return version(dist)
|
||||
|
||||
def get_model(self):
|
||||
"""
|
||||
Returns the :term:`app model` module.
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import warnings
|
||||
from unittest import TestCase
|
||||
|
@ -116,6 +117,88 @@ class TestAppHandler(TestCase):
|
|||
def test_get_title(self):
|
||||
self.assertEqual(self.app.get_title(), 'WuttJamaican')
|
||||
|
||||
def test_get_distribution(self):
|
||||
|
||||
# default should always be WuttJamaican (right..?)
|
||||
dist = self.app.get_distribution()
|
||||
self.assertEqual(dist, 'WuttJamaican')
|
||||
|
||||
# also works with "non-native" objects
|
||||
from config import Configuration
|
||||
config = Configuration({})
|
||||
dist = self.app.get_distribution(config)
|
||||
self.assertEqual(dist, 'python-configuration')
|
||||
|
||||
# can override dist via config
|
||||
self.config.setdefault('wuttatest.app_dist', 'importlib_metadata')
|
||||
dist = self.app.get_distribution()
|
||||
self.assertEqual(dist, 'importlib_metadata')
|
||||
|
||||
# but the provided object takes precedence
|
||||
dist = self.app.get_distribution(config)
|
||||
self.assertEqual(dist, 'python-configuration')
|
||||
|
||||
def test_get_distribution_pre_python_3_10(self):
|
||||
|
||||
# the goal here is to get coverage for code which would only
|
||||
# run on python 3,9 and older, but we only need that coverage
|
||||
# if we are currently testing python 3.10+
|
||||
if sys.version_info.major == 3 and sys.version_info.minor < 10:
|
||||
pytest.skip("this test is not relevant before python 3.10")
|
||||
|
||||
importlib_metadata = MagicMock()
|
||||
importlib_metadata.packages_distributions = MagicMock(
|
||||
return_value={
|
||||
'wuttjamaican': ['WuttJamaican'],
|
||||
'config': ['python-configuration'],
|
||||
})
|
||||
|
||||
orig_import = __import__
|
||||
|
||||
def mock_import(name, *args, **kwargs):
|
||||
if name == 'importlib.metadata':
|
||||
raise ImportError
|
||||
if name == 'importlib_metadata':
|
||||
return importlib_metadata
|
||||
return orig_import(name, *args, **kwargs)
|
||||
|
||||
with patch('builtins.__import__', side_effect=mock_import):
|
||||
|
||||
# default should always be WuttJamaican (right..?)
|
||||
dist = self.app.get_distribution()
|
||||
self.assertEqual(dist, 'WuttJamaican')
|
||||
|
||||
# also works with "non-native" objects
|
||||
from config import Configuration
|
||||
config = Configuration({})
|
||||
dist = self.app.get_distribution(config)
|
||||
self.assertEqual(dist, 'python-configuration')
|
||||
|
||||
# hacky sort of test, just in case we can't deduce the
|
||||
# package dist based on the obj - easy enough since we
|
||||
# have limited the packages_distributions() above
|
||||
dist = self.app.get_distribution(42)
|
||||
self.assertIsNone(dist)
|
||||
|
||||
# can override dist via config
|
||||
self.config.setdefault('wuttatest.app_dist', 'importlib_metadata')
|
||||
dist = self.app.get_distribution()
|
||||
self.assertEqual(dist, 'importlib_metadata')
|
||||
|
||||
# but the provided object takes precedence
|
||||
dist = self.app.get_distribution(config)
|
||||
self.assertEqual(dist, 'python-configuration')
|
||||
|
||||
# hacky test again, this time config override should win
|
||||
dist = self.app.get_distribution(42)
|
||||
self.assertEqual(dist, 'importlib_metadata')
|
||||
|
||||
def test_get_version(self):
|
||||
from importlib.metadata import version
|
||||
|
||||
# default should always be for WuttJamaican (right..?)
|
||||
self.assertEqual(self.app.get_version(), version('WuttJamaican'))
|
||||
|
||||
def test_make_title(self):
|
||||
text = self.app.make_title('foo_bar')
|
||||
self.assertEqual(text, "Foo Bar")
|
||||
|
|
Loading…
Reference in a new issue